1. 魔法方法是什么?
魔法方法(Magic Method)是Python比较独特的应用,它可以给你的类增加特殊的方法,如果你的对象实现了(重载),这些方法中的某一个,就会被Python所调用。正如装饰器类似,是对类/函数部分的提炼(装饰器是对关键功能部分的封装)。一般情况下,是对类中方法和属性一种提炼。
2. 魔法方法详细解释:
前面我们用过__init__,__repr__,__str__等魔法方法,我们在这里做一个总结,查遗补漏,由于这个位置在Python中比较松散的表示。这里做一个分别的描述。
2.1 基础魔法方法:
1. __new__(cls[, ...]) :初始化类构造函数
2.__init__(self[, ...]):初始化/构造函数
解释1:实例化对象时,第一个被调用的方法,其阐述直接传递给__init__方法处理,一般不会重写该方法。必须有一个参数clc,代表着要实例化的类,而且必须要有返回值,返回实例化出来的实例对象。好比制造产品之前的原材料采购环节。__init__好比原材料上进行加工的环节。
解释2:创建一个构造函数。
其中:1.来进行创建对象,这个new方法可以是object的,也可以是自己重写的,最后必须要return这个创建好的对象的引用。2.是进行实例对象的初始化,接收的就是new方法跑出来对象。
实例1:__init__(self)
这个方法就是一个构造函数,所谓构造函数的作用:1.通过类来进行传参,2.初始化类中的属性。
class Foo(object): def __init__(self,name): self.name = name f = Foo("Thomas")
实例2:type(第一个参数:类名,第二个参数:当前类的基类,第三个参数:类的成员)
上述代码中,obj是通过Foo类实例化的对象,其实,步进obj是一个对象,Foo类本身也是一个对象,因为在Python中一切皆是对象。如果按照一切事物都是对象的理论,obj对象是通过Foo类的构造方法创建的,那么Foo类对象应该也是通过执行某个类的构造方法创建的。
print(type(f)) # 表示,obj对象由Foo类创建 <class ‘__main__.Foo‘>
print(type(Foo)) # 表示,Foo类对象由type类创建 <class ‘type‘>
我们通过打印类型的值,obj对象由Foo类创建。Foo类对象由type类创建。所以,f对象是Foo类的一个实例,Foo类对象是type类的一个实例。即,Foo类对象是通过type类的构造方法创建的。
那么,创建类就可以有两种方式:
1.普通方式:
class Foo(object): def func(self): print("hello World")
2. 特殊方式:
def func(self): print("hello World") Foo = type(‘Foo‘,(object,),{‘func‘:func}) print(type(Foo)) # <class ‘type‘> f = Foo() f.func() # hello World
其中:‘func‘:func表示的是这个类中的方法,‘Foo‘,(object,)表示为类名和继承基类。通过type这个函数我们用特殊方式创建了一个类。
另外,我们还可以通过type的方式,把构造函数也纳入进来。
def func(self): print("hello World %s" % self.name) def __init__(self,name): self.name = name Foo = type(‘Foo‘,(object,),{‘func‘:func,‘__init__‘:__init__}) f = Foo("Thomas") f.func() # hello World Thomas
实例3:__new__:创建一个类的实例化
class Foo(object): # __metaclass__ = MyType # Foo通过__metaclass__来关联的 def __init__(self,name): self.name = name def __new__(cls, *args, **kwargs): print("Foo---new__") return object.__new__(cls) # 去继承父亲__new__方法 f = Foo("Thomas") print(f.name) # Foo---new__ # Thomas
从这个例子当中我们可以发现这样一个规律:
类——Foo这个对象——__init__这个对象,这样一个过程,因为一切皆对象,类也是个对象,__new__这个的作用就是把Foo这个对象装入object的这个类当中,其实在平时工作的时候很少写这个new函数。可以这么说,def __new__是吧Foo这个类放入进去了,然后,__init__这个有放入Foo这个类当中了。__new__起到的作用就是父类之前类的最本初状态!
我们在举几个相关的实例来理解这个__new__
例子1:我们来使用类属性来表示当前是否第一次创建
class Dog(object): __instance = True # True表示是第一次创建 __firstObj = None def __new__(cls): if cls.__instance: cls.__instance = False cls.__firstObj = object.__new__(cls) return cls.__firstObj # 如果是第一次创建 返回这个创建好的实例对象引用 else: # 不是第一次创建 那么返回第一次创建好的对象引用 return cls.__firstObj a = Dog() b = Dog() print( id(a) == id(b) ) # True
例子2:这样就实现了功能,但是我们注意到里面有两个类的属性,简化一下,其实可以写成一个直接让_instance为None的值,第一次创建好对象之后将引用赋值给__instance即可。
class Dog(object): __instance = None def __new__(cls): if cls.__instance == None: # None 就表示还没创建过对象 cls.__instance = object.__new__(cls) return cls.__instance # 如果是第一次创建 返回这个创建好的实例对象引用 else: # 不是第一次创建 那么返回第一次创建好的对象引用 return cls.__instance a = Dog() b = Dog() print( id(a) == id(b) )
例子3:我们发现,上面进行了多次的初始化对象,虽然只会创建一次失利对象,但是__init__()会被重复调用来初始化失利对象。如果我们现在如果只想让实例对象初始化一次,可以这么写。
class Dog(object): __instance = None __hasInit = False # 思路也是一样的 新增一个类属性表示是否第一次初始化 def __new__(cls): if cls.__instance == None: cls.__instance = object.__new__(cls) return cls.__instance else: return cls.__instance def __init__(self): if self.__hasInit == False: print(‘----init------‘) self.__hasInit = True a = Dog() # ----init------ b = Dog() # 不在重复初始化
实例4:__metaclass__下面代码在Py3中不能执行,在Py2中可以看到这个详细的过程
类默认是由type类实例化产生,type类中如何实现的创建类?类有事如何创建对象?类中有一个属性__metaclass__,其用来表示该类由谁来实例化创建,所以我们可以为__metclass__设置一个type类的拍成类,从而查看类的创建过程。
class MyType(type): def __init__(self,what,bases=None,dict=None): print("--MyType init---") super(MyType,self).__init__(what,bases,dict) def __call__(self, *args, **kwargs): print("--Mytype call--") obj = self.__new__(self,*args,**kwargs) self.__init__(obj,*args,**kwargs) class Foo(object): __metaclass__ = MyType # Foo通过__metaclass__来关联的 def __init__(self,name): self.name = name def __new__(cls, *args, **kwargs): print("Foo---new__") return object.__new__(cls) f = Foo("Thomas") print(f.name) # 先执行了:--MyType init--- # 后执行了:--Mytype call-- # 再执行了:Foo---new__" # 最后执行了:Thomas
小结:其实我们发现,我们在平时创建类的过程中,其实都是在用一个现存的模板来创建这个类(一般来说就是从__init__构造函数开始的)。其实在这个过程之前还有很多创建这个模板的过程。由此往前类推我的得到如下这样一个过程:
Py元类解释器——type元类——metaclass元类属性——__new__——模板——__init__其他等等... ...这样一个过程。
type----object---new---模板
我们前面用了type的方式去创建一个类的方式而不是用线程模板的这种方式去创建一个类为的就是要解释这样一个过程。
其中:在type元类(基类)中,我们通过__call__方法重新设计一个类,然后交给__new__看是否需要进一步修饰。其实在平时的工作中这个方法比较少用。但是底层的研究Py必须要知道。
3.__call__(self[, args...]) 允许一个类的实例像函数一样被调用:x(a, b) 调用 x.__call__(a, b),不需要返回值。
解释:如果一个类中定义了__call__方法,那么这个类它的实例就可以作为函数调用,也就实现了()运算符,即可以调用对象协议:
实例1:
class TmpTest: def __init__(self, x, y): self.x = x self.y = y def __call__(self, x, y): self.x, self.y = x, y a = TmpTest(1, 2) a(4, 5) print(a.x, a.y) # 5
实例2:借用装饰器来讲解一个__call__方法的使用,如果需要将一个类作为装饰器,那么需要为这个类实现这个方法:
class TmpTest: def __init__(self, func): self.func=func def __call__(self, *args,**kwargs): result=self.func(*args,**kwargs) return result @TmpTest def add_num(x,y): return x+y print(add_num(1,0)) # 1
4.__del__(self) 析构方法,当实例化对象被彻底销毁时被调用(实例化对象的所有指针都被销毁时被调用)
解释:很简单,相对于__init__构造函数这里就是一个西沟函数,用于在程序结束的时候释放内存空间,但是平时很少用,因为Py有自己的内存管理系统,会自动释放
实例1:
class Person(object): def run(self): print("run") def eat(self, food): print("eat" + food) def __init__(self,name,age,height,weight): self.name=name self.age=age self.height=height self.weight=weight def __del__(self):#当程序结束时运行 print("析构函数") per1=Person("lili",20,175,50) del per1 #手动释放对象 print(per1.name)#释放对象后程序不运行 #在函数里定义的对象,会在函数结束时自动释放,这样可以减少内存空间的浪费 def func(): per2=Person("x",2,45,7) func()
5.__repr__(self) 定义当被 repr() 调用时的行为
6. __str__(self) 定义当被 str() 调用时的行为
这两者都是用来实现类的订制:
方法5,在使用内建函数repr()进行自动调用,一般在黑屏终端中使用。
方法6,在使用print自动调用。
实例1:__str__
class Item(object): def __init__(self,name): self._name = name def __str__(self): return "Item‘s name is :" + self._name I = Item("Thomas") print(I) # Item‘s name is :Thomas
实例2:__repr__
class Item(object): def __init__(self,name): self._name = name def __repr__(self): return "Item‘s name is :" + self._name I = Item("Thomas") print(I) print(type(I)) # <class ‘__main__.Item‘> # Item‘s name is :Thomas
7. __len__(self) 定义当被 len() 调用时的行为
我们在编写类中的对象的时候想要用类名或者实例化的名字返回对象的长度,需要在类中添加这个方法,直接用len会报错。
实例1:
class Students(object): def __init__(self, *args): self.names = args def __len__(self): return len(self.names) s = Students(‘tom‘, ‘jack‘) print( len(s) ) # 2
8. __bytes__(self) 定义当被 bytes() 调用时的行为
只有很少的情景我们要把对象转换成为字节。在一些特殊的情况下,在吸入文件之前,我们需要将一个对象直接编码成字节。通常使用字符串并且使用str类型为我们体用字符串的字节表示惠更简单。注意,有一些__init__()方法会将一个数值类型的rank转换一个字符串,导致丢失原始的数值。为了使字节编码可逆,我们需要重新创建rank的原始数值。
实例1:通过__bytes__来实现,返回Card、rank和suit的UTF-8编码。
class Test(object): def __bytes__(self): class_code = self.__class__.__name__[0] rank_number_str = {‘A‘: ‘1‘, ‘J‘: ‘11‘, ‘Q‘: ‘12‘, ‘K‘: ‘13‘}.get(self.rank, self.rank) string = "(" + " ".join([class_code, rank_number_str, self.suit, ]) + ")" return bytes(string, encoding="utf8")
这种实现首先用字符串表示Card对象,然后将字符串编码为字节。通常是最简单最灵活的方法。
实例2:当我们拿到一串字节时,我们可以将这串字节解码为一个字符串,然后将字符串转换为一个新的Card对象。下面是基于字节创建Card对象的方法。
def card_from_bytes( buffer ): string = buffer.decode("utf8") assert string[0 ]=="(" and string[-1] == ")" code, rank_number, suit = string[1:-1].split() class_ = { ‘A‘: AceCard, ‘N‘: NumberCard, ‘F‘: FaceCard }[code] return class_( int(rank_number), suit )
# 我们可以像下面这样生成一个Card对象的字节表示。 b= bytes(someCard) # 我们可以用生成的字节重新创建Card对象。 someCard = card_from_bytes(b)
9. __hash__(self) 定义当被 hash() 调用时的行为
解释:与__eq__类似,但是!如果这个类无法作为哈希集合的元素使用,比如hashable collections指的是:set,frozenset和dict就不能用__eq__了。也属于中比较运算符。
class Person: def __init__(self, name, age): self.name = name self.age = age def __hash__(self): print(self.name, ‘使用了__hash__方法‘) return hash(self.name) # def __eq__(self, other): # print(self.name, ‘使用了__eq__方法‘) # return self.__dict__ == other.__dict__ person1 = Person(‘zs‘, 20) person2 = Person(‘ls‘, 20) person3 = Person(‘ww‘, 30) person4 = Person(‘zs‘, 20) set1 = {person1, person2, person3, person4} # zs 使用了__hash__方法 # ls 使用了__hash__方法 # ww 使用了__hash__方法 # zs 使用了__hash__方法
10. __bool__(self) 定义当被 bool() 调用时的行为,应该返回 True 或 False
class Test1(object): pass class Test2(object): def __bool__(self): return False def __len__(self): return 0 class Test3(object): def __bool__(self): return False # def __len__(self): # return 0 test1 = Test1() test2 = Test2() test3 = Test3() print(bool(test1)) # 没有定义bool和len对象,默认为True print(bool(test2)) # bool函数决定布尔值,返回False print(bool(test3)) # 有bool函数,则由len函数决定bool值,返回False #True #False #False
11. __format__(self, format_spec) 定义当被 format() 调用时的行为
实例1:
class formatest(object): def __init__(self, name, age): self.name,self.age = name, age def __format__(self,specification): if specification == "": return str(self) strformat = specification.replace("%s",self.name).replace("%r",self.age) return strformat if __name__ == "__main__": people = formatest("Thomas", "89") print ("{}".format(people)) print ("{0:%s-%r}".format(people)) print (format(people, "%s-%r")) # <__main__.formatest object at 0x000001CDB4848F60> # Thomas-89 # Thomas-89
2.2 属性相关方法:
12. __getattr__(self, name) 定义当用户试图获取一个不存在的属性时的行为
13. __getattribute__(self, name) 定义当该类的属性被访问时的行为
实例1:__getattr__,当属性查找不到时会报错,如果重载__getattr在查找不到属性的时候就不会报错了。
from datetime import date class User(object): def __init__(self,name,birthday): self.name = name self.birthday = birthday if __name__ == ‘__main__‘: user = User("hobby",date(year=1987,month=1,day=1)) print(user.age) # Traceback (most recent call last): # File "F:/QUANT/练习/temp.py", line 10, in <module> # print(user.age) # AttributeError: ‘User‘ object has no attribute ‘age‘
from datetime import date class User(object): def __init__(self,name,birthday): self.name = name self.birthday = birthday def __getattr__(self, item): return "not find attr" if __name__ == ‘__main__‘: user = User("hobby",date(year=1987,month=1,day=1)) print(user.age) # not find attr
实例2:如果我们写错了属性名字,还可以通过__getattr__修正过来。
from datetime import date class User(object): def __init__(self,name,birthday): self.name = name self.birthday = birthday def __getattr__(self, item): return self.name if __name__ == ‘__main__‘: user = User("hobby",date(year=1987,month=1,day=1)) print(user.Name) # hobby
实例3:我们调用了user.company,但是在company在info里面,不能直接访问,这样我们可以在__getattr__写逻辑让user.company直接访问。
from datetime import date class User(object): def __init__(self,name,birthday,info): self.name = name self.birthday = birthday self.info = info def __getattr__(self, item): return self.info[item] if __name__ == ‘__main__‘: user = User("hobby",date(year=1987,month=1,day=1),info={"company":"CCTV"}) print(user.company) # CCTV
实例4:__getattribute__。当每次调用属性时,Py都会无条件的进入这个里面,不论属性存在与否,这就是和__getattr__的区别。
from datetime import date class User(object): def __init__(self,name,birthday,info): self.name = name self.birthday = birthday self.info = info def __getattribute__(self, item): return "hobby1" if __name__ == ‘__main__‘: user = User("hobby",date(year=1987,month=1,day=1),info={"company":"CCTV"}) print(user.name) print(user.age) # 调用存在的属性 # hobby1 # hobby1
实例5:必须特别小心getattribute方法,因为Py在查找类的方法名称时对其进行调用。
from datetime import date class User(object): def __init__(self,name,birthday,info): self.name = name self.birthday = birthday self.info = info def __getattribute__(self, item): raise ArithmeticError def get_name(self): print(self.name) if __name__ == ‘__main__‘: user = User("hobby",date(year=1987,month=1,day=1),info={"company":"CCTV"}) user.get_name() # 会产生报错。 # Traceback (most recent call last): # File "F:/QUANT/练习/temp.py", line 16, in <module> # user.get_name() # File "F:/QUANT/练习/temp.py", line 10, in __getattribute__ # raise ArithmeticError # ArithmeticError
14. __get__(self, instance, owner) 定义当描述符的值被取得时的行为
15. __set__(self, instance, value) 定义当描述符的值被改变时的行为
16. __delete__(self, instance) 定义当描述符的值被删除时的行为
解释:get表示调用一个苏醒是触发,set表示为一个属性赋值时触发,del表示删除属性时触发。
概念:描述符:描述符就是一个新式类,在这新式类中,至少实现了这三种方法。描述符的作用是用来代理另外一个类的属性的(必须把面舒服定义成这个类的类属性,不能定义到构造函数中)
实例1:
class Celsius: def __get__(self, instance, owner): return 5 * (instance.fahrenheit - 32) / 9 def __set__(self, instance, value): instance.fahrenheit = 32 + 9 * value / 5 class Temperature: celsius = Celsius() def __init__(self, initial_f): self.fahrenheit = initial_f t = Temperature(212) print(t.celsius) t.celsius = 0 print(t.fahrenheit) # 100.0 # 32.0
实例2:
class RevealAccess(object): """A data descriptor that sets and returns values normally and prints a message logging their access. """ def __init__(self, initval=None, name=‘var‘): self.val = initval self.name = name def __get__(self, obj, objtype): print ‘Retrieving‘, self.name return self.val def __set__(self, obj, val): print ‘Updating‘, self.name self.val = val >>> class MyClass(object): ... x = RevealAccess(10, ‘var "x"‘) ... y = 5 ... >>> m = MyClass() >>> m.x Retrieving var "x" >>> m.x = 20 Updating var "x" >>> m.x Retrieving var "x" >>> m.y
Python笔记_第四篇_高阶编程_魔法(术)方法详解(重载的再详解)
原文:https://www.cnblogs.com/noah0532/p/10932758.html