目录
类的特殊方法
属性 含义 __name__ 类、函数、方法等的名字 __module__ 类定义所在的模块明 __class__ 对象或类所属的类 __bases__ 类的基类元组,顺序为他们在基类列表中出现的顺序 __doc__ 类、函数的文档字符串,如果没有定义则为None __mro__ 类的mro,class.mro()返回结果保存在__mro__中 __dict__ 类或实例的属性,可写字典 __subclasses__() 类的子类列表,class.__subclasses__()
查看属性
方法 意义 __dir__ 返回类或者对象的所有成员名称列表。dir()函数就是调用__dir__()。如果提供__dir__(),则返回属性的列表,否则会尽量从__dict__属性中手机信息
- 如果dir([obj])参数obj包含方法【__dir__()】,该方法将被调用。如果参数obj不包含【__dir__()】该方法将最大限度的收集参数信息
- 【dir()】对于不同类型的对象具有不同的行为
- 如果对象是模块对象,返回的列表包含模块的属性名
- 如果对象是类型或者类对象,返回的列表包含类的属性名,及它的基类的属性名
- 否则,返回列表包含对象的属性名,它的类的属性名和类的基类的属性名
#导入模块的dir import test1 print(dir(test1)) # ['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__'] #当前模块的dir print(dir()) # ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'test1'] #函数的dir def foo(): x = 1 pass print(dir(foo)) # ['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__'] #类、类的继承的dir class A: a = 123 def __init__(self,y): self.y = y pass print(sorted(dir(A))) # ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a'] print(sorted(A.__dict__)) # ['__dict__', '__doc__', '__init__', '__module__', '__weakref__', 'a'] class B(A): pass print(sorted(dir(B))) # ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a'] print(sorted(B.__dict__)) # ['__doc__', '__module__'] #类实例的对象 b = B(6) print(sorted(dir(b))) # ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a', 'y'] print(sorted(b.__dict__)) #['y'] #原理,在对象中收集 --》 找到类的属性收集 --》 找到父类的属性收集 --》 一直到object的类属性 print(sorted(set(b.__dict__.keys()) | set(b.__class__.__dict__.keys() ) | set(object.__dict__))) # ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'y'] #类的__dir__(self)方法,只对实例有影响 class C(A): def __dir__(self): return ["123123"] print(sorted(dir(c))) # ['123123'] print(sorted(c.__dict__)) # []
魔术方法
魔术方法分类
- 创建、初始化、销毁【__init__ 与 __del__】
- hash
- bool
- 可视化
- 运算符重载
- 容器和大小
- 可调用对象
- 上下文管理
- 反射
- 描述器
- 其他杂项
hash
方法 意义
__hash__ 内建函数 hash( ) 调用的返回值,返回一个整数。如果定义这个方法该类的实例就可hash __eq__ 对应 == 操作符,判断2个对象是否相等,返回bool值 看下面代码:
class A: def __init__(self,name,age=18): self.name = name def __hash__(self): return 1 print(hash(A("tom"))) # 1 print((A("tom"),A("tom"))) # (<__main__.A object at 0x01B119D0>, <__main__.A object at 0x01B11930>) print([A("tom"),A("tom")]) # [<__main__.A object at 0x01B119D0>, <__main__.A object at 0x01B11930>] s = {A("tom"),A("tom")} #集合,为什么没有去重 print(s) # {<__main__.A object at 0x01B119D0>, <__main__.A object at 0x01B11930>}
上面的set为什么没有被去重?
class A: def __init__(self,name,age=18): self.name = name def __hash__(self): return 1 def __eq__(self, other): #判断两个对象是否相等 return self.name == other.name print(hash(A("tom"))) # 1 print((A("tom"),A("tom"))) # (<__main__.A object at 0x01B119D0>, <__main__.A object at 0x01B11930>) print([A("tom"),A("tom")]) # [<__main__.A object at 0x01B119D0>, <__main__.A object at 0x01B11930>] s = {A("tom"),A("tom")} #集合,为什么没有去重 print(s) # {<__main__.A object at 0x018872F0>} print(len(s)) # 1
【__eq__】方法只是返回一个hash值作为set的key,但是去重还需要【__eq__】来判断2个对象是否相等。
hash值相等,只是hash冲突,不能说明两个对象是相等的
因此,一般来说提供【__hash__】方法是为了作为set或dict的key,所以去重要同时提供【__eq__】方法
不可hash对象【isinstance(A,collections.abc.Hashable)一定为False】,去重需要提供【__eq__】方法
print(isinstance(A,collections.abc.Hashable)) # True
思考:list类的实例为什么不可hash?
- 源码中有一句【__hash__ = None】,也就是如果调用【__hash__()】相当于None(),一报错。所有类都继承object,而这个类具有【__hash__()】方法的,如果一个类不能被hash,就把【__hash__】设置为None
练习:设计二维坐标类Point,使其称为2个坐标的实例是否相等?
import collections.abc class Point: def __init__(self,x,y): self.x = x self.y = y def __hash__(self): return hash((self.x,self.y)) def __eq__(self, other): if self is other: return True return self.x == other.x and self.y == other.y p1 = Point(4,5) p2 = Point(4,5) print(hash(p1)) # 1888254024 print(hash(p2)) # 1888254024 print(p1 is p2) # False print(p1 == p2) # True print(id(p1),id(p2)) # 8551920 14663280 print(set((p1,p2))) # {<__main__.Point object at 0x01297DF0>} print(isinstance(p1,collections.abc.Hashable)) # True
bool、可视化
方法 意义 __bool__ 内建函数bool(),或者对象放在逻辑表达式的位置,调用这个函数返回布尔值。没有定义【__bool__()】就找【__len__()】返回长度,非0为真。如果【__len__()】也没有定义,那么所有实例都返回真 __repr__ 内建函数repr()对一个对象获取字符串表达式。调用【__repr__】方法返回字符串表达,如果【__repr__】也没有定义,就直接返回object的定义就是显示内存地址信息 __str__ str()函数、内建函数format(),print()函数调用,需要返回对象的字符串表达。如果没有定义,就去调用【__repr__】方法返回字符串表达,如果【__repr__】没有定义,就直接返回对象的内存地址信息 __bytes__ bytes()函数调用,返回一个对象的bytes表达,即返回bytes对象 class A :pass print(bool(A())) # True if A(): print("Real A") class B: def __bool__(self): return False print(bool(B)) # True print(bool(B())) # False class C: def __len__(self): return 0 print(bool(C())) # False
- 这也是为什么空字典、空字符串、空元组、空集合、空列表等效为False的原因
可视化
方法 意义 __repr__ 内建函数repr()对一个对象获取字符串表达式。调用【__repr__】方法返回字符串表达,如果【__repr__】也没有定义,就直接返回object的定义就是显示内存地址信息 __str__ str()函数、内建函数format(),print()函数调用,需要返回对象的字符串表达。如果没有定义,就去调用【__repr__】方法返回字符串表达,如果【__repr__】没有定义,就直接返回对象的内存地址信息 __bytes__ bytes()函数调用,返回一个对象的bytes表达,即返回bytes对象 class A: def __init__(self,name,age=18): self.name = name self.age = age def __repr__(self): return "__repr__ : {} {} ".format(self.name,self.age) def __str__(self): return " __str__ : {} {} ".format(self.name,self.age) def __bytes__(self): return " {} is {} ".format(self.name,self.age).encode() print(A("tom")) # print函数使用 __str__ : tom 18 print([A("tom")]) # [ ] 使用__str__,但其内部使用 [__repr__ : tom 18 ] print(([str(A("tom"))])) # [ ]使用__str__,str()函数也使用__str__ [' __str__ : tom 18 '] print("str:a") # str:a s = '1' # 1 print(s) print(["a"],(s,)) # ['a'] ('1',) print({s,'a'}) # {'1', 'a'} print(bytes(A("tom"))) # b' tom is 18 '
运算符重载
Operator模块提供以下的特殊方法,可以将类的实例使用下面的操作符来操作
运算符 特殊方法 含义 <,<= , == ,>,>= ,!= __lt__、__le__、__eq__、__gt__、__ge__、__ne__ 比较运算符 +、-、*、/、%、//、**、divmod __add__、__sub__、__mul__、__truediv__、__mod__、__floordiv__、__pow__、__divmod__ 算数运算符,移位,位运算也有对应的方法 +=、-=、*=、/=、%=、//=、**= __iadd__、__isub__、__imul__、__itruediv__、__imod__、__ifoordiv__、__ipow__ class A: def __init__(self,name,age = 18): self.name = name self.age = age def __sub__(self, other): return self.age - other.age def __isub__(self, other): return A(self.name,self-other) #返回一个新的对象 tom = A("tom") jerry = A("jerry",16) print(tom -jerry) # 2 print(jerry - tom,jerry.__sub__(tom)) # -2 -2 print(id(tom)) tom -=jerry # 24100496 print(tom.age,id(tom)) # 2 24100592
练习:完成Point类设计,实现判断点相等的方法,并完成向量的加法
- 在直角坐标系里面,定义原点为向量的起点,两个向量和与差的坐标分别等于这两个向量相应坐标的和与差若向量的表示为(x,y)形式
class Ponit: def __init__(self,x,y): self.x = x self.y = y def __eq__(self, other): return self.x == other.x and other.y == other.y def __add__(self, other): return Ponit(self.x + other.x,self.y + other.y) def add(self,other): return (self.x + other.x , self.y + other.y) def __str__(self): return "Point : {} {} ".format(self.x,self.y) p1 = Ponit(1,2) p2 = Ponit(1,2) points = (p1,p2) print(points[0].add(points[1])) # (2, 4) #运算符重载 print(points[0] + points[1]) # Point : 2 4 print(p1 == p2) # True
运算符重载应用场景
往往是用面向对象实现的类,需要做大量的运算,而运算符是这种运算在数学上最常见的表达方式。例如,上例中对+进行了运算符重载,实现了Point类的二元操作,重新定义为Point+,Point。
提供运算符重载,比直接提供加法要更加适合该领域内使用者的习惯。int类,几乎实现了所有操作符,可以作为参考
@functools.total_rodering 装饰器
- 【__lt__】、【__le__】、【__eq__】、【__gt__】、【__ge__】、是比较大小必须实现的方法,但是全部写完太麻烦,使用@functools.total_ordering装饰器可以大大简化代码
- 但是要求【__eq__】必须实现,其他方法【__lt__】、【__le__】、【__gt__】、【__ge__】实现其一即可
from functools import total_ordering @total_ordering class Person: def __init__(self,name,age): self.name = name self.age = age def __eq__(self, other): return self.age == other.age def __gt__(self, other): return self.age > other.age tom = Person("tom",20) jerry = Person("jerry",16) print(tom > jerry) # True print(tom < jerry) # False print(tom >= jerry) # True print(tom <= jerry) # False
- 上例中大大简化了代码,但是一般来说比较实现等于或者小于方法也够了,其他可以不实现,所以这个装饰器只是看着很美好,且可能会带来性能问题,建议需要什么方法就自己创建,少用这个装饰器
class Person: def __init__(self,name,age): self.name = name self.age = age def __eq__(self, other): return self.age == other.age def __gt__(self, other): return self.age > other.age def __ge__(self, other): return self.age >= other.age tom = Person("tom",20) jerry = Person("jerry",16) print(tom > jerry) # True print(tom < jerry) # False print(tom >= jerry) # True print(tom <= jerry) # False
容器相关
方法 意义 __len__ 内建函数len(),返回对象的长度(>=0的整数),如果把对象当作容器类型看,就如同lis或者dict,bool()函数调用的时候,如果没有__bool__()方法,则会看__len__()方法是否存在,存在返回非0为真 __iter__ 迭代容器时,调用,返回一个新的迭代器对象
__contains__ in 成员运算符,没有实现,就调用 __iter__ 方法遍历 __getitem__ 实现key[key]方法,序列对象,key接受整数为索引,或者切片。对于set和dict,key为hashable,key不存在引发KeyError异常 __setitem__ 和__getitem__ 的访问类似,是设置值的方法 __missing__ 字典或其子类使用__getitem__() 调用时,key不存在执行该方法 练习:将购物车类改造成方便操作的容器类
class Item: def __init__(self,name,**kwargs): self.name = name self._spac = kwargs def __repr__(self): return "{} = {}".format(self.name,self._spac) class Cart: def __init__(self): self.items = [] def __len__(self): return len(self.items) def additem(self,item): self.items.append(item) def __setitem__(self, key, value): self.items[key] = value def __iter__(self): return iter(self.items) def __getitem__(self, index): return self.items[index] def __missing__(self, key): #字典和set没有key时调用次方法 return "Key = {}".format(key) def __add__(self, other): self.items.append(other) return self def __repr__(self): return str(self.items) def __str__(self): return str(self.items) cart = Cart() print(len(cart)) # 0 print(cart + 2 +4) # [2, 4] print(len(cart)) # 2 for i in cart: print(i) # 2 # 4 print(cart[1]) # 4 cart[1] = 499 print(cart) # [2, 499]