目录
面向对象高级编程
-
__slots__
- 限制实例的属性,
__slots__
更多的是用来作为一个内存优化工具 - 当实例化几万个对象的时候,每个对象都会生成一个名称空间dict,而每一个名称空间都会各自占用一个内存,造成内存的浪费,用__slots__时候就不再产生dict了,从而省空间
-
class Student(object): __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
__slots__
定义的属性仅对当前类实例起作用,对继承的子类是不起作用的
- 限制实例的属性,
-
@property
- 我们可以给类添加set和get方法,访问__xx的变量,用于参数校验,@property更简便
class Student(object):
@property
def score(self):
return self._score
@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
s.score = 60 #设置
s.score #获取
"""
不定义setter方法就是一个只读属性
"""
-
多重继承
- 通过多重继承,一个子类可以同时获得多个父类的所有功能。
- MixIn。需要“混入”额外的功能,设计通常称之为MixIn
class Dog(Mammal, RunnableMixIn, CarnivorousMixIn):
pass
-
定制类(__str__、__iter__...)
- __str__。print打印对象输出的内容,不用print打印,调用的是__repr__
- __str__给用户看,__repr__用于调试服务的
- __iter__。类变成类似可迭代对象,执行iter()会调用此方法,返回一个迭代器
- __next__。查看这里就明白了:迭代器
-
class Fib(object): def __init__(self): self.a, self.b = 0, 1 # 初始化两个计数器a,b def __iter__(self): return self # 实例本身就是迭代对象,故返回自己 def __next__(self): self.a, self.b = self.b, self.a + self.b # 计算下一个值 if self.a > 100000: # 退出循环的条件 raise StopIteration() return self.a # 返回下一个值 """ __iter__()方法,该方法返回一个迭代对象, Python的for循环就会不断调用该迭代对象的__next__()方法拿到循环的下一个值 直到遇到StopIteration错误时退出循环 """
- __getitem__。a[b],索引方式获取值时候调用
-
class Fib(object): def __getitem__(self, n): if isinstance(n, int): # n是索引 a, b = 1, 1 for x in range(n): a, b = b, a + b return a if isinstance(n, slice): # n是切片 start = n.start stop = n.stop if start is None: start = 0 a, b = 1, 1 L = [] for x in range(stop): if x >= start: L.append(a) a, b = b, a + b return L f = Fib() f[5] #索引 f[0:5] #切片
- __setitem__。 a[b] = c,索引方式设置值时候调用
- __ delitem__。del a[b],索引方式删除时候调用
- __getattr__。a.b,点方式获取时候调用
- 方式会先获取实例属性,没有的话找类属性,再不存在就去调用__getattr__方法
- 私有属性__xx就相当于不存在(其他属性没影响),会直接调用此方法
- getattr()。这方法找属性也是上面这个步骤,getattr(obj,'attr',default)
- 只是找不到时候返回一个默认值
- 而且他的attr是一个字符串形式
- __getattribute__。任何的点方式访问任何的属性都会调用此方法(除了类调用类属性)
- 此方法先于__getattr__方法调用。顺序是这样的,先访问__getattribute__,如果发现属性不存在,抛出AttributeError异常,如果这时候我们实现了__getattr__方法,就会调用__getattr__方法,不会报错,如果没实现就抛出AttributeError异常。
-
""" 但是要防止递归调用 """ class man(object): def __getattribute__(self, attr): return self.__dict__[attr] # 这种方式肯定是递归调用了,self.__dict__[attr],又是访问自己了 """ 应该去访问基类或者超类的方法 """ class man(object): def __getattribute__(self, attr): return super().__getattribute__(attr)
- __setattr__。a.b =c,点方式设置时候调用
- 设置特殊属性(如__dict__)是不会调用此方法的
-
""" 所以我们才可以这样 """ class test_attr(object): def __setattr__(self,key,value): self.__dict__[key] = value
-
- setattr()。这个方法和a.b方法效果一样,但是,获取属性时候输入的是字符串形式,这时候你可以动态生成属性了,非常有用
- 设置特殊属性(如__dict__)是不会调用此方法的
- __delattr__。del a.b,点方式删除时候调用
- __call__。对象可被调用
- __del__。del obj。每当 del 实例化对象时会触发
- 此方法一般无须定义,一般无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的
- __enter__、__exit__。上下文管理器
- __doc__。类注释信息就放在这个特殊变量里面,如下的“jgh”
-
class test(object): """jgh""" pass
-
- __str__。print打印对象输出的内容,不用print打印,调用的是__repr__
-
枚举类(比写类变量好用)
from enum import Enum
class Color(Enum):
red = 1
orange = 2
yellow = 3
green = 4
blue = 5
indigo = 6
purple = 7
"""
1、枚举取值
1)通过成员的名称来获取成员
Color['red']
2)通过成员值来获取成员
Color(2)
3)通过成员,来获取它的名称和值
red_member = Color.red
red_member.name
red_member.value
2、可迭代
for color in Color:
print(color)
如果枚举有值重复的成员,循环遍历枚举时只获取值重复成员的第一个成员
如果想把值重复的成员也遍历出来,要用枚举的一个特殊属性__members__
for color in Color.__members__.items():
print(color)
3、枚举比较
Color.red is Color.red
olor.blue == Color.red
1.1 定义枚举时,成员名称不允许重复
1.2 默认情况下,不同的成员值允许相同。但是两个相同值的成员,第二个成员的名称被视作第一个成员的别名
1.3 如果枚举中存在相同值的成员,在通过值获取枚举成员时,只能获取到第一个成员
1.4 如果要限制定义枚举时,不能定义相同值的成员。可以使用装饰器@unique
"""
-
object、type、metaclass
-
object
-
type类
- 一切皆对象,一切对象皆由type创建
- 动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运行时动态创建的。
- class的定义是运行时动态创建的,而创建class的方法就是使用
type()
函数- 当我们定义一个class
-
class MyClass: data = 1
- python真正执行的是下面这段代码,就是 type 的
__call__
运算符 -
class = type(classname, superclasses, attributedict)
- 然后进一步调用
-
type.__new__(typeclass, classname, superclasses, attributedict) type.__init__(class, classname, superclasses, attributedict)
-
def fn(self, name='world'): # 先定义函数 print('Hello, %s.' % name) Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello 类 print(type(Hello)) # <class 'type'> """ 要创建一个class对象,type()函数依次传入3个参数: 1、class的名称; 2、继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法; 3、class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上 正常的 Hello 定义,和你手工去调用 type 运算符的结果是完全一样的 """
-
元类metaclass
- metaclass 是 type 的子类,通过替换 type 的
__call__
运算符重载机制,“超越变形”正常的类 - 我们来看看metaclass的执行过程就能一目了然了
-
class Mymeta(type): def __init__(self, name, bases, dic): super().__init__(name, bases, dic) print('===>Mymeta.__init__') print(self.__name__) print(dic) print(self.yaml_tag) def __new__(cls, *args, **kwargs): print('===>Mymeta.__new__') print(cls.__name__) return type.__new__(cls, *args, **kwargs) def __call__(cls, *args, **kwargs): print('===>Mymeta.__call__') obj = cls.__new__(cls) cls.__init__(cls, *args, **kwargs) return obj class Foo(metaclass=Mymeta): yaml_tag = '!Foo' def __init__(self, name): print('Foo.__init__') self.name = name def __new__(cls, *args, **kwargs): print('Foo.__new__') return object.__new__(cls) """ 输出: ===>Mymeta.__new__ Mymeta ===>Mymeta.__init__ Foo {'__module__': '__main__', '__qualname__': 'Foo', 'yaml_tag': '!Foo', '__init__': <function Foo.__init__ at 0x0000000007EF3828>, '__new__': <function Foo.__new__ at 0x0000000007EF3558>} !Foo """ foo = Foo('foo') """ 输出 ===>Mymeta.__call__ Foo.__new__ Foo.__init__ """
- 从上面的运行结果可以发现在定义 class Foo() 定义时,会依次调用 MyMeta 的
__new__
和__init__
方法构建 Foo 类,其中__new__就会返回type的实例,也就是我们定义的类Foo,然后在调用 foo = Foo() 创建类的实例对象时,才会调用 MyMeta 的__call__
方法,__call__方法里面调用 Foo 类的__new__
和__init__
方法 -
""" metaclass可以给我们自定义的MyList增加一个add方法 metaclass是类的模板,所以必须从`type`类型派生 """ class ListMetaclass(type): def __new__(cls, name, bases, attrs): attrs['add'] = lambda self, value: self.append(value) return type.__new__(cls, name, bases, attrs) class MyList(list, metaclass=ListMetaclass): pass """ 当我们传入关键字参数metaclass时 __new__()方法接收到的参数依次是和type是一样的: 1、当前准备创建的类的对象; 2、类的名字; 3、类继承的父类集合; 4、类的方法集合。 """
- 通过metaclass修改类定义的意义。ORM就是一个典型的例子。
- ORM全称“Object Relational Mapping”,即对象-关系映射,就是把关系数据库的一行映射为一个对象,也就是一个类对应一个表
-
""" 利用meteclass动态修改类来实现ORM 以下是我们期待的写法 """ class User(Model): # 定义类的属性到列的映射: id = IntegerField('id') name = StringField('username') email = StringField('email') password = StringField('password') # 创建一个实例: u = User(id=12345, name='Michael', email='test@orm.org', password='my-pwd') # 保存到数据库: u.save() """ 首先来定义Field类,它负责保存数据库表的字段名和字段类型 """ class Field(object): def __init__(self, name, column_type): self.name = name self.column_type = column_type def __str__(self): return '<%s:%s>' % (self.__class__.__name__, self.name) """ 在Field的基础上,进一步定义各种类型的Field,比如StringField,IntegerField """ class StringField(Field): def __init__(self, name): super(StringField, self).__init__(name, 'varchar(100)') class IntegerField(Field): def __init__(self, name): super(IntegerField, self).__init__(name, 'bigint') """ 定义ModelMetaclass """ class ModelMetaclass(type): def __new__(cls,name,bases,attrs): # 排除掉对Model类的修改 if name == 'Model': return type.__new__(cls,name,bases,attrs) print('Found model %s' % name) # 将Field类型的属性保存在mapping中 mapping = dict() for k,v in attrs.items(): if isinstance(v,Field): print('Add Field %s to mapping' % v.name) mapping[k] = v # 从类属性中删除该Field属性 for k in mapping.keys(): attrs.pop(k) attrs['__mapping__'] = mapping #保存属性和列的映射关系 attrs['__table__'] = name return type.__new__(cls,name,bases,attrs) """ 定义Model类,修改dict的类创建方式,基于Modelclass """ class Model(dict,metaclass=ModelMetaclass): def __init__(self,**kw): super().__init__(**kw) def __getattr__(self, key): try: return self[key] except KeyError: raise AttributeError(r"'Model' object has no attribute '%s'" % key) def __setattr__(self, key, value): self[key] = value #定义save方法保存到数据库 def save(self): fields = list() args = list() for k, v in self.__mapping__.items(): fields.append(v.name) args.append(getattr(self, k, None)) #根据定义的类属性名,查找值,保存在args中 #sql = 'insert into %s (%d) values (%s)' % (self.__table__, ','.join(fields), ','.join(args)) print('SQL: %s' % fields) print('ARGS: %s' % str(args)) """ getattr(self,k,None)的效果是:先获取实例的属性;若无,则获取类的属性;若无,则调用__getattr__方法。 对u调用getattr方法,因为u没有实例属性,而类属性已经通过attrs.pop(k)删掉, 因此会调用__getattr__,该方法返回u的键值 """
- metaclass 是 type 的子类,通过替换 type 的
-