Python 元类编程
函数是对象
# ==== 函数也是对象 ==== def func(): pass print(func.__class__) ==== 我们还可以为函数对象进行增加属性的操作:虽然没人这样做 ==== func.x = "随便写的: x" func.y = 随便写的: y" print(func.x) (func.y) ==== 执行结果 ==== Ps:可以看到,函数是类function的实例对象。这充分验证了Python中一切皆对象的概念。 """ <class 'function'> 随便写的: x 随便写的: y """ 类也是对象
==== 类也是对象 ==== class Foo(object): print(Foo.) print(object.print(dict.print(list. ==== 执行结果 ==== Ps:可以看见,不管是自定义的类,还是Python自带的类,甚至是object类,都是type类的实例对象。 <class 'type'> <class 'type'> <class 'type'> <class 'type'> """ 什么是元类
浅析class内部机制一个类定义后发生的4件事
==== 常规定义类 ==== People(object): def __init__(self,name,age): self.name = name self.age = age say(self): print({0}:{1}.format(self.name,self.age)) ==== 测试 ==== print(People.__dict__) p1 = People(Yunya",18) p1.say() ==== 执行结果 ==== {'__module__': '__main__','__init__': <function People.__init__ at 0x0000029FBF9DFF70>,'say': <function People.say at 0x0000029FC7DA6700>,'__dict__': <attribute '__dict__' of 'People' objects>,'__weakref__': <attribute '__weakref__' of 'People' objects>,'__doc__': None} Yunya:18 """ ==== 一个类定义后发生的4件事 ==== 类有三大特征: 1、类名 class_name = People 2、类的基类 class_bases = (object,) <-- 注意,必须逗号分隔。tuple类型 3、执行类体代码拿到类的命名空间 class_dic = {} <-- 类的局部命名空间 class_body = def __init__(self,age): self.name = name self.age = age def say(self): print("{0}:{1}".format(self.name,self.age)) """ exec(class_body,{},class_dic) 位置1:执行的代码,位置2:全局命名空间,位置3,执行代码期间出现的局部命名空间的变量全部存放于此 print(class_dic) 该字典中存放了类中的变量名对应的内存地址。 4、调用元类 People = type(class_name,class_bases,class_dic) ==== 测试 ==== p1 = People() p1.say() 可以成功执行! ==== 执行结果 ==== Ps:虽然可以用这种方式来实例化出类对象,但是我们并不推荐这样做。相比于直接使用class机制,这样做法显得复杂并且对比class_dic与People.__dict__来看明显使用class机制会完善的多。 {'__init__': <function __init__ at 0x000001908A7B61F0>,'say': <function say at 0x000001908AA0FF70>} Yunya:18 """ exec:三个参数 参数一:包含一系列python代码的字符串 参数二:全局作用域(字典形式),如果不指定,默认为globals() 参数三:局部作用域(字典形式),如果不指定,默认为locals() 可以把exec命令的执行当成是一个函数的执行,会将执行期间产生的名字存放于局部名称空间中 g={ 'x':1,y':2 } l={} exec(''' global x,z x=100 z=200 m=300 '''print(g) {'x': 100,'y': 2,'z':200,......} print(l) {'m': 300} 自定义元类元类如何生产出类对象
==== 元类如何生产出类对象 ==== class MetaClass(type): <-- 必须继承type类,才会被视为一个元类 自定义元类""" __new__(cls,*args,**kwargs): cls ---> 类本身(空的) args ---> ("类名",(类的继承关系,),{类的局部命名空间}) kwargs ---> 空 """ MetaClass.__new__ --> cls nMetaClass.__new__ --> args nMetaClass.__new__ --> kwargs n return super().__new__(cls,**kwargs) # 注意,此时必须将属性全部传入。因为现在的对象是个空对象,或者可以采取下面的方式 return type. 让父类 type 为我们造出空对象 __init__(self,1)">MetaClass.__init__ --> self nMetaClass.__init__ --> args nMetaClass.__init__ --> kwargs n__init__(*args,1)"> 让父类 __init__ 为我们将空对象转换为类对象 class People(object,metaclass=MetaClass): MetaClass.__new__ --> cls <class '__main__.MetaClass'> MetaClass.__new__ --> args ('People',(<class 'object'>,{'__module__': '__main__','__qualname__': 'People','__init__': <function People.__init__ at 0x000001FC34DC7670>,'say': <function People.say at 0x000001FC34DC7790>}) MetaClass.__new__ --> kwargs {} MetaClass.__init__ --> self <class '__main__.People'> MetaClass.__init__ --> args ('People','say': <function People.say at 0x000001FC34DC7790>}) MetaClass.__init__ --> kwargs {} """ 元类如何生产出类对象的实例对象
==== 有__call__方法 ==== __call__(self,1)">kwargs): 调用了__call__) p1() ==== 执行结果 ==== Ps:没有抛出异常了。 调用了__call__ """ ==== 无__call__方法 ==== ==== 执行结果 ==== Ps:由于People类中没有定义__call__方法,则其实例化对象不能使用 名字() 的方式进行调用 Traceback (most recent call last): File "C:/Users/Administrator/PycharmProjects/learn/反射与自省.py",line 15,in <module> p1() TypeError: 'People' object is not callable """ ==== 元类如何生产出类对象的实例对象 ==== self --> 类对象本身(空的) <class '__main__.People'> args --> 传入的参数。 ('Yunya',18) kwargs --> 空的,{} # 构造出空对象 obj = self.__new__(self,1)">kwargs) print(obj) <__main__.People object at 0x0000020AEC3A02E0> __new__过后的对象的__dict__) 将空对象转换为类对象People的实例对象 self.__init__(obj,1)">__init__过后的对象的__dict__ 将类对象People的实例对象进行返回 return obj return object.__new__(cls) 相当于调用元类下的 __call__ (p1.name) (p1.age) <__main__.People object at 0x0000020AEC3A02E0> __new__过后的对象的__dict__ {} __init__过后的对象的__dict__ {'name': 'Yunya','age': 18} Yunya 18 """ 图片来源: 实例对象与类对象的查找顺序类对象的属性查找顺序
==== 类对象的属性查找顺序 ==== """ name = MetaClass A(object): name = "A" B(A): name = "B" class C(B,1)">MetaClass): (C.name) MetaClass """ 实例对象的属性查找顺序
==== 实例对象属性查找顺序 ==== pass c1 = C() (c1.name) """ 练习题类的命名规则检测""" kwargs): if not args[0].istitle(): raise NameError(类名必须是大写!) super(MetaClass,1)">kwargs) class student(object,1)"> Traceback (most recent call last): File "C:/Users/Administrator/PycharmProjects/learn/元类编程.py",line 11,in <module> class student(object,metaclass=MetaClass): File "C:/Users/Administrator/PycharmProjects/learn/元类编程.py",line 6,in __init__ raise NameError("类名必须是大写!") NameError: 类名必须是大写! """ 类的文档信息检测if __doc__" not in args[2]: raise SyntaxError({0}类必须要有文档注释".format(args[0])) SyntaxError 语法错误 super(MetaClass,in __init__ raise SyntaxError("{0}类必须要有文档注释".format(args[0])) # SyntaxError 语法错误 SyntaxError: student类必须要有文档注释 """ 基于元类实现单例模式单例:即单个实例,指的是同一个类实例化多次的结果指向同一个对象,用于节省内存空间 # 如果我们从配置文件中读取配置来进行实例化,在配置相同的情况下,就没必要重复产生对象浪费内存了settings.py文件内容如下 HOST='1.1.1.1' PORT=3306 方式一:定义一个类方法实现单例模式 import settings Mysql: __instance=None host self.port=port @classmethod singleton(cls): not cls.__instance: 如果没有就实例化出一个,如果有就直接返回 cls.__instance=cls(settings.HOST,settings.PORT) 进行 Mysql.__init__(settings.HOST,settings.PORT)) 并赋值 return cls.__instance obj1=Mysql(1.1.1.2',3306) obj2=Mysql(print(obj1 is obj2) False obj3=Mysql.singleton() obj4=Mysql.singleton() print(obj3 is obj4) True 方式二:定制元类实现单例模式 Mymeta(type): 定义类Mysql时就触发 事先先从配置文件中取配置来造一个Mysql的实例出来 self.__instance = object.__new__(self) 产生对象 self.__init__(self.__instance,settings.HOST,settings.PORT) 初始化对象 上述两步可以合成下面一步 self.__instance=super().__call__(*args,**kwargs) super().(name,dic) Mysql(...)时触发 if args or kwargs: args或kwargs内有值 obj=object.(self) self.kwargs) obj return self.__instance class Mysql(metaclass=Mymeta): port obj1=Mysql() 没有传值则默认从配置文件中读配置来实例化,所有的实例应该指向一个内存地址 obj2=Mysql() obj3=Mysql() is obj2 is obj3) obj4=Mysql(1.1.1.4) 方式三:定义一个装饰器实现单例模式 def singleton(cls): cls=Mysql _instance=cls(settings.HOST,settings.PORT) def wrapper(*args,1)">or kwargs: obj=cls(*args,1)"> _instance wrapper @singleton Mysql=singleton(Mysql) Mysql: port obj1=Mysql() obj2=is obj3) True obj4=Mysql(1.1.1.3) obj5=Mysql(False ? (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |