__new__方法很少使用,即使创建了该方法,也会使用return super().new(cls)基类object的__new__方法来创建实例并返回。
class Person: def __init__(self, name, age=18): self.name = name self.age = age def __str__(self): return 'str: {} {}'.format(self.name, self.age) def __repr__(self): return 'repr: {} {}'.format(self.name, self.age) def __bytes__(self): return 'bytes: {} {}'.format(self.name, self.age).encode() a = Person(name='tom') print(a) # <__main__.Person object at 0x00000000027F0FD0> # str: tom 18 print(str(a)) # <__main__.Person object at 0x00000000027F0FD0> print(str(a).encode()) # b'str: tom 18' print(bytes(a)) # b'bytes: tom 18' print(repr(a)) # repr: tom 18 print([a]) # [repr: tom 18] print(str([])) 列表中又调用repr print((a, )) # (repr: tom 18,) print('{}'.format(a)) # str: tom 18 # 没有str方法,会找repr方法,但是没有repr方法,直接找基类的注意不能通过判断是否带引号来判断输出值的类型,类型判断要使用type或isinstance
__hash__方法,内建函数hash() 调用的返回值,返回一个整数。如果定义这个方法该类的实例就可hash。(整数的hash算法是取模)
class Person: def __init__(self, name, age=18): self.name = name self.age = age def __hash__(self): return 1 def __eq__(self, other): return self.age == other.age # 这样写的话,p1 == p2 ——>True def __repr__(self): return '<Person {} {}>'.format(self.name, self.age) print(hash(1), hash(2), hash(5000000)) # 整数的hash算法是取模,除数是62位的整数 print(hash('abc')) print(hash(('abc', ))) print(hash(Person)) print(hash(Person('tom'))) p1 = Person('tom') p2 = Person('jerry') print(hash(p1), hash(p2)) print({123, 123}) # {123}去重了 print({p1, p2}) # {<Person tom 18>, <Person jerry 18>} # 没有去重,是因为p1和p2的内容不同(虽然他们的hash值相同);去重两个条件内容相同,hash值相同 print('~~~~~~~~~~', p1 == p2) # 会调用__eq__方法 print({(123, ), (123, )}) # {(123,)} 去重了set去重需要两个条件: 内容相同,hash值相同 __hash__方法只是返回一个hash值作为set的key,但是去重,还需要__eq__方法来判断两个对象是否相等,hash值相等,只是hash冲突,不能说明两个对象是相等的。 因此,一般来说提供__hash__方法是为了作为set或者dict的key,如果去重,要同时提供__eq__方法。不可hash对象isinstance(p1, collections.Hashable)一定为False
1.list类实例为什么不可hash? list类中(源码)直接将__hash__设置为None<;也就是调用__hash__()相当于调用None(),一定会报错。所有类都是继承自object,而这个类是具有__hash__方法的,如果一个类不能被hash,就把__hash_设为None 2.functools.lru_cache使用到的functools._HashedSeq类继承自list,为什么可hash? 因为子类重写了hash函数,将父类的hash方法覆盖了,这个方法实际上计算的是元组的hash值
设计二维坐标类Point,使其成为可hash类型,并比较两个坐标的实例是否相等
class Point: def __init__(self, x, y): self.x = x self.y = y def __repr__(self): return '<Point {},{}>'.format(self.x, self.y) def __eq__(self, other): return self.x == other.x and self.y == other.y def __hash__(self): return hash((self.x, self.y)) p1 = Point(12, 14) p2 = Point(34, 65) p3 = Point(12, 14) print(p1) # <Point 12,14> print(p2) print(hash(p1)) print(hash(p2)) print(p1 == p2) # False print(p1 == p3) # True__isub__方法定义,一般会in-place就地修改自身;如果没有定义__isub__方法,则会调用__sub__方法
完成Point类设计,实现判断点相等的方法,并完成向量的加法
class Point: def __init__(self, x, y): self.x = x self.y = y def add(self, other): return self.__class__(self.x + other.x, self.y + other.y) # # def __add__(self, other): # return self.add(other) __add__ = add def __iadd__(self, other): self.x += other.x self.y += other.y return self # 可以链式 def __eq__(self, other): return self.x == other.x and self.y == other.y def __repr__(self): return '<Point {},{}>'.format(self.x, self.y) p1 = Point(1, 2) p2 = Point(3, 4) print(p1 + p2) # <Point 4,6> p1 += p2 print(id(p1), p1) # 32150416 <Point 4,6> print(p1 + p2 + p2) # <Point 10,14>往往是用面向对象实现的类,需要做大量的运算,而运算符是这种运算在数学上最常见的表达式,例如上例中的+进行了运算符重载,实现了Point类的二元操作,重新定义为Point + Point。 提供运算符重载,比直接提供加法方法更加适合该领域内使用者的习惯。 int类,几乎实现了所有操作符,可以作为参考。
lt,le,eq,gt,ge,是比较大小必须实现的方法,但是全部写完太麻烦了,使用@functools.total_ordering装饰器,就可以大大简化代码。
但是要求__eq___必须实现,其他方法实现其一
from functools import total_ordering @total_ordering class A: def __init__(self, values): self.values = values def __eq__(self, other): return self.values == other.values def __gt__(self, other): return self.values > other.values a = A(10) b = A(8) print(a == b) print(a != b) print(a > b, a < b, a >= b, a <= b)上例中大大简化了代码,但是一般来说比较实现等于或者小于方法也就够了,其他可以不实现,所以这个装饰器只是看着很美好,且可能带来性能问题,建议需要什么方法就自己创建,少用这个装饰器。
将购物车类改造成方便操作的容器类
class Cart: def __init__(self): self.items = [] def add(self, item): self.items.append(item) return self def __add__(self, other): self.add(other) return self def __len__(self): return len(self.items) def __getitem__(self, index): # print(index) return self.items[index] # self[index]会无限递归 def __setitem__(self, index, value): # 一般不需要返回值 self.items[index] = value # self[index] = value # 不能这么写,会无限递归 def __repr__(self): return '<Cart {} {}>'.format(__class__.__name__, self.items) def __iter__(self): # return iter(self.items) # for i in self.items: # yield i yield from self.items c1 = Cart() c1.add(1) c1.add(2) c1.add(1).add(2) print(c1) c1 + 4 + 5 print(c1) print(len(c1)) print(c1[0]) c1[1] = 100 print(c1[1]) for z in c1: print(z)