输出:
12
- 实现了这两个方法,可以用于获得长度,用[]访问数据,甚至切片和循环
import collections ''' collections.namedtuple:用于构建一个只有属性没有方法的简单类 参数: 1、类名 2、一个字符串列表,表示各个属性 ''' Card = collections.namedtuple('Card', ['rank', 'suit']) class Deck(): # +号可以用于列表的连接 ranks = [str(i) for i in range(2,11)] + list('JQKA') suits = ['spades', 'hearts', 'clubs', 'diamonds'] def __init__(self): # 双重for循环产生笛卡尔积 self._cards = [Card(rank,suit) for suit in self.suits for rank in self.ranks ] def __len__(self): return len(self._cards) def __getitem__(self, position): return self._cards[position] deck = Deck(); # 调用__len__ print(len(deck)) # 调用__getitem__ print(deck[0]) # 切片,继续调用__getitem__ print(deck[:3]) print(deck[12::13]) print('-' * 100) # for循环,还是调用__getitem__ for d in deck: print(d) print('-' * 100) # 排序 suit_values = {'spades':3,'hearts':2,'clubs':1,'diamonds':0} def order(card): rank_value = Deck.ranks.index(card.rank) return rank_value*len(suit_values)+suit_values[card.suit] for d in sorted(deck,key=order): print(d)输出:52Card(rank='2', suit='spades')[Card(rank='2', suit='spades'), Card(rank='3', suit='spades'), Card(rank='4', suit='spades')][Card(rank='A', suit='spades'), Card(rank='A', suit='hearts'), Card(rank='A', suit='clubs'), Card(rank='A', suit='diamonds')]----------------------------------------------------------------------------------------------------Card(rank='2', suit='spades')Card(rank='3', suit='spades')Card(rank='4', suit='spades')……Card(rank='Q', suit='diamonds')Card(rank='K', suit='diamonds')Card(rank='A', suit='diamonds')----------------------------------------------------------------------------------------------------Card(rank='2', suit='diamonds')Card(rank='2', suit='clubs')Card(rank='2', suit='hearts')Card(rank='2', suit='spades')……Card(rank='A', suit='diamonds')Card(rank='A', suit='clubs')Card(rank='A', suit='hearts')Card(rank='A', suit='spades')
- 加号和乘号都是中缀运算符,基本原则是不改变操作对象,而是产出一个新的值
from math import hypot class Vector: def __init__(self,x,y): self.x=x self.y=y # 在repr中被调用 # 如果没有实现__str__,print使用__repr__替代 def __repr__(self): return 'Vector({0},{1})'.format(self.x,self.y) def __abs__(self): return hypot(self.x,self.y) # 重载加号需要实现这个方法 # 注意,重载这个方法之后,只能实现a*b,而不能实现b*a def __add__(self, other): if not isinstance(other,Vector): raise TypeError('only permits Vector!') return Vector(self.x+other.x,self.y+other.y) # 默认情况下,bool(自定义类的实例)总是为true,除非自定义类实现__bool__方法 # 如果没有实现__bool__方法,bool(x)会尝试调用x.__len__(),若返回0,bool为False,否则为True def __bool__(self): return bool(abs(self)) # 实现向量与实数的乘法 # 同样,重载这个方法只能实现Vector*n,而不能实现n*Vector def __mul__(self, other): return Vector(self.x*other,self.y*other) v1 = Vector(3,4) print('v=',v1) print('-' * 100) print('abs(v)=',abs(v1)) print('-' * 100) v2 = Vector(4,5) print('Vector(3,4)+Vector(4,5)=',v1+v2) print('-' * 100) print('Vector(3,4)*3=',v1*3)输出:v= Vector(3,4)----------------------------------------------------------------------------------------------------abs(v)= 5.0----------------------------------------------------------------------------------------------------Vector(3,4)+Vector(4,5)= Vector(7,9)----------------------------------------------------------------------------------------------------Vector(3,4)*3= Vector(10.5,14.0)
- "{:格式说明}".format(obj) 或 format(obj, 格式说明)的调用中,python将格式说明传递给obj.__format__函数作为第二个参数
class Man: def __init__(self, name, age): self.name, self.age = name, age def __format__(self, format_spec): if format_spec == "": return str(self) result = format_spec.replace('%name', self.name).replace('%age', self.age) return result class People: def __init__(self, *people): self.people = list(people) def __format__(self, format_spec): if format_spec == "": return str(self) # 如果不直接使用格式说明,而是传递给另外的类,需要用到格式化字符串的嵌套 return ",".join('{0:{1}}'.format(c,format_spec) for c in self.people) # 这样调用效果相同 # return ",".join(format(c,format_spec) for c in self.people) alice = Man("Alice", "18") bob = Man("Bob", "19") candy = Man("Candy", "20") ''' 这里:后面的“%name is %age years old.”是格式说明 它匹配Man类__format__(self, format_spec)的第二个形参format_spec ''' print('{:%name is %age years old.}'.format(alice)) print('-' * 100) # 这样调用效果相同 print(format(alice,'%name is %age years old.')) print('-' * 100) # 嵌套使用格式说明 ppl = People(alice,bob ,candy) print("{:%name is %age years old}".format(ppl))输出:Alice is 18 years old.----------------------------------------------------------------------------------------------------Alice is 18 years old.----------------------------------------------------------------------------------------------------Alice is 18 years old,Bob is 19 years old,Candy is 20 years old
TrueTrue
room size= 150room door= 5-------------------- 我是分割线 --------------------__setitem__(window:6)__getitem__(window)room[window]= 6-------------------- 我是分割线 --------------------__contains__(window)True__delitem__(window)__contains__(window)False
- 首先我觉得还是要先理清楚Iterable和Iterator的关系,我们先来看一下类的层次关系
class Iterator(Iterable) | Method resolution order: | Iterator | Iterable | builtins.object | | Methods defined here: | | __iter__(self) | | __next__(self) | Return the next item from the iterator. When exhausted, raise StopIteration class Iterable(builtins.object) | Methods defined here: | | __iter__(self)
- 可以看到Iterator继承了Iterable,它们都需要实现__iter__函数,Iterator多出了一个__next__
- 说说个人理解吧,Iterator是真正用于迭代的迭代器对象,所以它必须实现__next__,客户函数可以通过next()函数取得它的下一个值。它的__iter__函数通常返回自己,毕竟自己就是个迭代器么
- 而Iterable是一个自定义的随便什么类,其实和迭代器没什么关系。可能它正好有一些值,你如果想让客户按一定次序访问这些值,就实现__iter__方法,返回一个上面说的迭代器给客户用。那么我们把实现了__iter__函数,自己又不是迭代器的对象,称为可迭代对象,即Iterable
- 这个函数也返回一个迭代器,即Iterator,但产生数据的方式是反过来的,所以如果想让客户用正反两个顺序访问一个Iterable里的数据,就实现两个Iterator,一个通过__iter__返回,一个通过__reversed__返回
谈了这么多,看一个具体例子吧
from collections.abc import Iterable,Iterator # WeekDay的正向Iterator class WeekDayiterator(): def __init__(self, idx, data): self.index = idx self.data = data def __iter__(self): return self def __next__(self): result = self.data[self.index] self.index = (self.index + 1) % len(self.data) return result # WeekDay的反向Iterator class WeekDayiterator_reversed(): def __init__(self, idx, data): self.index = idx self.data = data def __iter__(self): return self def __next__(self): result = self.data[self.index] self.index = (self.index - 1) % len(self.data) return result # 自定义的Iterable类 class WeekDay(): data = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'] def __init__(self,idx=0): self.index=idx def __iter__(self): return WeekDayiterator(self.index,self.data) def __reversed__(self): return WeekDayiterator_reversed(self.index,self.data) print('WeekDay is Iterable:',issubclass(WeekDay,Iterable)) print('WeekDay is Iterator:',issubclass(WeekDay,Iterator)) print('WeekDayiterator is Iterator:',issubclass(WeekDayiterator,Iterator)) print('WeekDayiterator_reversed is Iterator:',issubclass(WeekDayiterator_reversed,Iterator)) print('-'*100) wd1 = WeekDay() wd1_iter = iter(wd1) for i in range(10): print(next(wd1_iter)) print('-'*100) wd1_reversed = reversed(wd1) for i in range(10): print(next(wd1_reversed))输出:WeekDay is Iterable: TrueWeekDay is Iterator: FalseWeekDayiterator is Iterator: TrueWeekDayiterator_reversed is Iterator: True----------------------------------------------------------------------------------------------------SundayMondayTuesdayWednesdayThursdayFridaySaturdaySundayMondayTuesday----------------------------------------------------------------------------------------------------SundaySaturdayFridayThursdayWednesdayTuesdayMondaySundaySaturdayFriday
- 实现了__call__的类,实例出的对象是可调用对象,即可以在对象后面加(),像函数那样调用它
class Add_To_Box: box=[] def __call__(self, *args, **kwargs): self.box.extend(args) add_to_box = Add_To_Box() add_to_box('Cat','Dog') add_to_box('Pig') print(','.join(add_to_box.box))输出:Cat,Dog,Pig- 实现这两个魔术方法,可以使用with语法,让Python自动在建立对象后和with语句全部结束前分别调用对象的这两个方法
class OpenBox: box = ['lots of gold','lots of shit'] password = 'show me money' input='' def __init__(self,pswd): print('Box is created!') self.input=pswd def __enter__(self): print('Box is opened!') if self.input == self.password: return self.box[0] else: return self.box[1] def __exit__(self, exc_type, exc_val, exc_tb): print('Box is closed!') with OpenBox('show me money') as box: print(box) print('-'*100) with OpenBox('wrong password') as box: print(box)输出:Box is created!Box is opened!lots of goldBox is closed!----------------------------------------------------------------------------------------------------Box is created!Box is opened!lots of shitBox is closed!
- __new__在__init__之前调用,__new__是一个类方法
- 构造方法返回的实例,是由__new__先产生,然后传递给__init__初始化的
- 在重写__new__的时候,如果想返回这个类的实例,需要调用object.__new__
- 一个单例模式的例子
class Singleton: def __new__(cls, *args, **kwargs): print('__new__({0}) is called'.format(cls)) if not hasattr(cls,'instance'): cls.instance = object.__new__(cls) return cls.instance def __init__(self): print('__init__({0}) is called'.format(self)) s1 = Singleton() s2 = Singleton() print(s1 == s2)输出:__new__(<class '__main__.Singleton'>) is called__init__(<__main__.Singleton object at 0x0000024BD91242E8>) is called__new__(<class '__main__.Singleton'>) is called__init__(<__main__.Singleton object at 0x0000024BD91242E8>) is calledTrue
- 用点号运算符访问对象继承树中不存在的属性时自动调用__getattr__
- 使用obj.key=value方式设置属性时自动调用__setattr__,注意在__setattr__中要设置属性,需要使用self.__dict__,否则将出现无限递归
- 用点号运算符访问对象属性时,调用__getattribute__
- 注意在__getattribute__中访问自己的属性,需要使用 super().__getattribute__(attr_name),否则将无限递归
- 有了__getattribute__就不会访问__getattr__
class AttrException(Exception): pass class Animal: leg = 4 # privates里面保存私有属性 privates=[] def __init__(self,name): # 这里也会调用__setattr__() self.name = name def __getattr__(self, item): print('__getattr__({0}) is called'.format(item)) # 私有属性不让访问 if item in self.privates: raise AttrException("Private attribute!") try: return self.__dict__[item] except KeyError: print('{0} is not existing'.format(item)) def __setattr__(self, key, value): print('__setattr__({0},{1}) is called'.format(key,value)) self.__dict__[key] = value class Dog(Animal): pass class Tiger(Animal): privates = ['bottom'] def __getattribute__(self, item): print('__getattribute__({0}) is called'.format(item)) if item in super().__getattribute__('privates'): raise AttrException("Private attribute!") return super().__getattribute__(item) dog = Dog('Puppy') # 对已有属性不调用 __getattr__ dog.name # 访问不存在的属性,调用__getattr__ dog.age print('-' * 100) tiger = Tiger('King') # 对已有属性仍然调用__getattribute__ tiger.name tiger.bottom = "Don't touch" try: # 调用__getattribute__,发现是私有属性,抛错 tiger.bottom except AttrException as exc: print(exc.__repr__())输出:__setattr__(name,Puppy) is called__getattr__(age) is calledage is not existing----------------------------------------------------------------------------------------------------__setattr__(name,King) is called__getattribute__(__dict__) is called__getattribute__(name) is called__setattr__(bottom,Don't touch) is called__getattribute__(__dict__) is called__getattribute__(bottom) is calledAttrException('Private attribute!')
- 属性描述符是一个类,有点特殊的类,它至少实现了__get__方法,也可以实现__set__甚至__delete__方法
- 属性描述符一般只会在另外一个类中使用,被当做那个类的类属性,而且一定要是类属性哦,因为它必须存在于类的__dict__中。
- 如果它存在于实例的__dict__中,Python不会去自动调用__get__和__set__,于是就废了
- 只实现了__get__方法的属性描述符被称为Non-Data Descriptor,同时实现了__set__的属性描述符被称为Data Descriptor
- 同名属性的访问顺序:
1、Data Descriptor
2、实例属性
3、Non-Data Descriptor
- 也就是说,实例属性会覆盖Non-Data Descriptor,但不会覆盖Data Descriptor
class DataDescriptor: def __get__(self, instance, owner): print('__get__({0}, {1}, {2})'.format(self,instance,owner)) def __set__(self, instance, value): print('__set__({0}, {1}, {2})'.format(self,instance,value)) class NonDataDescriptor: def __get__(self, instance, owner): print('__get__({0}, {1}, {2})'.format(self,instance,owner)) class AttributeOrder: descriptor1 = DataDescriptor() descriptor2 = NonDataDescriptor() def __init__(self,d1,d2): self.descriptor1 = d1 self.descriptor2 = d2 ao = AttributeOrder('d1','d2') # DataDescriptor不会被实例属性覆盖 print(ao.descriptor1) # NonDataDescriptor会被实例属性覆盖 print(ao.descriptor2)
转载于:https://www.cnblogs.com/StackNeverOverFlow/p/9775525.html
相关资源:python魔术方法指南