Python之路(第二十八篇) 面向对象进阶:类的装饰器、元类

一、类的装饰器

类作为一个对象,也可以被装饰。

例子

1def wrap(obj): 2 print("装饰器-----") 3 obj.x = 1 4 obj.y = 3 5 obj.z = 5 6 return obj 78 @wrap #将Foo类作为一个参数传入装饰器函数wrap,返回同时返回该对象,把新对象重新命名为Foo 9 #即 Foo = wrap(Foo) 10 class Foo: 11 pass 1213 #执行结果: 14 #装饰器----- 1516 print(Foo.__dict__) #输出结果可以看到,新的Foo类新增了x,y,z属性

函数可以作为一个对象,也有__dict__方法

1def wrap(obj): 2 print("装饰器-----") 3 obj.x = 1 4 obj.y = 3 5 obj.z = 5 6 return obj 78 @wrap #test = wrap(test) 9 def test(): 10 print("test-----") 11 test.x = 10 #test的x属性被重新赋值 12 print(test.__dict__) #输出结果可以看到,test作为一个函数也有__dict__方法, 13 # 新的test函数新增了x,y,z属性

类的装饰器应用

例子

1class Type: 23 def __init__(self,key,except_type): #People对象的key,和期望的数据类型 4 self.key = key 5 self.except_type = except_type 67 def __get__(self, instance, owner): 8 return isinstance.__dict__[self.key] 910 def __set__(self, instance, value): 11 print("instance---",instance) 12 if not isinstance(value,self.except_type): 13 print("您输入的类型不是%s"%self.except_type) 14 raise TypeError 15 instance.__dict__[self.key] = value 1617 def __delete__(self, instance): 18 isinstance.__dict__.pop(self.key) 1920 def deco(**kwargs): 21 def wrapper(obj): #类的装饰器 22 for key,val in kwargs.items(): 23 setattr(obj,key,Type(key,val)) #设置people类对象的每个参数的描述符 24 return obj 25 return wrapper 2627 @deco(name=str,age=int) 28 class People: 2930 def __init__(self,name,age): 31 self.name = name 32 self.age = age 333435 p = People("nick",18) 36 print(p.__dict__)

二、自定义property

装饰器也可以是一个类,在自定义property中要使用一个类作为装饰器

例子

1class LazyProperty: 23 def __init__(self, func): 4 self.func = func 56 def __get__(self, instance, owner): 7 print("执行__get__") 8 if not instance: # 如果是用原类.属性来调用,,这时instance(对象)值为None,直接返回描述符对象 9 return self 10 res = self.func(instance) # 执行传入的函数属性,并把原对象作为参数传入 11 return res 121314 class Room: 1516 def __init__(self, name, length, width): 17 self.name = name 18 self.length = length 19 self.width = width 2021 @LazyProperty # 这里相当于执行了area = LazyProperty(area),这里的azyProperty(area)其实是非数据描述符, 22 # 新的area已经是经过类LazyProperty装饰过的函数地址 23 def area(self): 24 return self.length * self.width 252627 r = Room("nick", 18, 10) 28 print(r.area) # 执行对象的方法,先在对象的属性字典里寻找,没有则在非数据描述符里寻找,找到非数据描述符里的__get__方法。

实现延迟计算功能,即实现计算一次再次调用不再进行计算

1class LazyProperty: 23 def __init__(self, func): 4 self.func = func 56 def __get__(self, instance, owner): 7 print("执行__get__") 8 if not instance: # 如果是用原类.属性来调用,,这时instance(对象)值为None,直接返回描述符对象 9 return self 10 value = self.func(instance) # 执行传入的函数属性,并把原对象作为参数传入 11 setattr(instance,self.func.__name__,value) #将每次调用的函数属性名字和值存入对象的__dict__, 12 # self.func.__name__是获取被调用函数属性的名字 13 return value 141516 class Room: 1718 def __init__(self, name, length, width): 19 self.name = name 20 self.length = length 21 self.width = width 2223 @LazyProperty # 这里相当于执行了area = LazyProperty(area),这里的azyProperty(area)其实是非数据描述符, 24 # 新的area已经是经过类LazyProperty装饰过的函数地址 25 def area(self): 26 return self.length * self.width 272829 r = Room("nick", 18, 10) 30 print(r.__dict__) 31 print(r.area) # 执行对象的方法,先在对象的属性字典里寻找,没有则在非数据描述符里寻找,找到非数据描述符里的__get__方法。 32 print(r.__dict__)

三、property补充

一个静态属性property本质就是实现了get,set,delete三种方法

用语法糖可以实现property的类似属性的设置和删除,与一般的属性设置删除没有区别

1class People: 23 def __init__(self): 4 self.study = "8h" 56 @property 7 def study(self): 8 print("获取study,执行描述符的__get__方法") 9 return self.val 10 # return self.study #无线递归 1112 @study.setter 13 def study(self,value): 14 print("执行__set__方法") 15 self.val = value 1617 @study.deleter 18 def study(self): 19 print("执行__delete__方法") 20 del self.val 2122 p = People() 23 print(p.study) #获取对象的study属性 ,self.study实际上是存在self.val里 24 p.study = "10h" #执行property描述符的__set__方法,设置对象的属性, 25 print(p.__dict__) 26 del p.study #执行property描述符的__delete_方法,删除对象的属性 27 print(p.__dict__)

四、元类

exec()函数

exec:三个参数

参数一:字符串形式的命令

参数二:全局作用域(字典形式),如果不指定,默认就是用全局 globals()

参数三:局部作用域(字典形式),如果不指定,默认就是用局部 locals()

exec会在指定的局部作用域内执行字符串内的代码,除非明确地使用global关键字

例子

1g = {"x":1,"y":2} 2 l = {"a":100} 34 exec(""" 5 global x,y 6 x = 10 7 y = 100 8 z = 100 9 """,g,l) # exec 当成一个函数的执行,需要指定全局作用域和局部作用域 10 print(g) #在输出结果中可以看到x,y的值发生了变化 11 print(l) #新增加的作为局部作用域的属性

定义类的两种方式

(1)用class关键字定义类

1 定义类的方式一:用class关键字定义 ​ 2 3class People: 45 country = "china" 6 def __init__(self,name): 7 self.name = name 89 def talk(self): 10 print("在说话")

(2)手动模拟class创建类的过程:将创建类的步骤拆分开,手动去创建

准备工作:

创建类主要分为三部分

a 类名

b 类的父类

c 类体

1class_name = "People" #设置类名 2 class_bases = (object,) #设置类的父类 3 class_body = """ 4 country = "china" 5 def __init__(self,name): 6 self.name = name 78 def talk(self): 9 print("在说话") 1011 """ 12 class_dic = {} 13 exec(class_body,globals(),class_dic) #这样执行一下得到类的名称空间(属性字典) 14 print(class_dic) 15 People2 = type(class_name,class_bases,class_dic) #用元类创建了类

什么是元类?

在python中,一切皆对象,一般的类也是一个类的对象,即这种起源、开始的类就称作元类。

用class关键字定义的类本身也是一个对象,负责产生该对象的类称之为元类(元类可以简称为类的类),内置的元类为type。

元类的参数

元类实例化创建一般的类有三个参数

1、类名class_name="xxx"

2、基类们class_bases=(object,),要继承的父类名,用元组

3、类的名称空间class_dic,类的名称空间是执行类体代码而得到的,就是类的属性字典

调用type时会依次传入以上三个参数

例子

1class Foo: #一般的用class 关键字创建的类 2 pass 34 f = Foo() 5 print(Foo) 6 print(Foo.__dict__) 78 t = type("FFo",(object,),{}) #由元类创造出来的一般类 9 print(t) 10 print(t.__dict__)

输出结果

1<class '__main__.Foo'> 2 {'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None} 3 <class '__main__.FFo'> 4 {'__module__': '__main__', '__dict__': <attribute '__dict__' of 'FFo' objects>, '__weakref__': <attribute '__weakref__' of 'FFo' objects>, '__doc__': None} 5

元类创建类也可以添加属性和方法

例子2

1class Foo: 2 x = 1 3 def __init__(self,name): 4 self.name = name 567 f = Foo("nick") 8 print(Foo) 9 print(Foo.__dict__) 101112 def __init(self,name): 13 self.name = name 1415 t = type("FFo",(object,),{"__init__":__init,"x":1}) #可以直接在属性字典里为创建的类添加属性和方法 16 print(t) 17 print(t.__dict__) 18 t=T("nick") 19 print(t.__dict__)

输出结果

1<class '__main__.Foo'> 2 {'__module__': '__main__', 'x': 1, '__init__': <function Foo.__init__ at 0x00665DB0>, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None} 3 <class '__main__.FFo'> 4 {'__init__': <function __init at 0x00665DF8>, 'x': 1, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'FFo' objects>, '__weakref__': <attribute '__weakref__' of 'FFo' objects>, '__doc__': None} 5 {'name': 'nick'}

自定义元类

一个类没有声明自己的元类,默认它的元类就是type,除了使用元类type,用户也可以通过继承type来自定义元类

例子1

1class MyType(type): 23 def __init__(self,class_name,class_bases,class_dic): 4 print("类名",class_name) 5 print("基类",class_bases) 6 print("类字典",class_dic) 7 super().__init__(class_name,class_bases,class_dic) 8910 class People(object,metaclass=MyType): #这里metaclass = MyType就执行MyType("People",(ovject,),{}), 11 # 然后自动实例化调用MyType的__init__方法 12 def __init__(self,name,age): 13 self.name = name 14 self.age = age 1516 def talk(self): 17 print("在说话。。。") 1819 p = People("nick",18) 20 print(p.__dict__)

输出结果

1类名 People 2 基类 (<class 'object'>,) 3 类字典 {'__module__': '__main__', '__qualname__': 'People', '__init__': <function People.__init__ at 0x00625DF8>, 'talk': <function People.talk at 0x00625DB0>} 4 {'name': 'nick', 'age': 18} 5

自定义元类控制类的创建

例子1

对新建的类名的要求

1class MyMeta(type): 23 def __init__(self,class_name,class_bases,class_dic): 4 if not class_name.istitle(): #判断产生的类的首字母是不是大写 5 raise TypeError("类的首字母不是大写") 6 super().__init__(class_name,class_bases,class_dic) 78 class people(metaclass=MyMeta): #程序运行到这里会直接报错 910 def __init__(self,name,age): 11 self.name = name 12 self.age = age 1314 def talk(self): 15 print("在说话")

例子2

要求新建的类必须要有注释

1class MyMeta(type): 23 def __init__(self,class_name,class_bases,class_dic): 4 print(class_dic["__doc__"],bool(class_dic["__doc__"])) 5 if not class_dic["__doc__"].strip() or "__doc__" not in class_dic : #判断新建类没有注释或者注释是空的 6 raise TypeError("新建类没有注释") 78 super().__init__(class_name,class_bases,class_dic) 910 class people(metaclass=MyMeta): #程序运行到这里会直接报错 11 """ 12 """ 13 def __init__(self,name,age): 14 self.name = name 15 self.age = age 1617 def talk(self): 18 print("在说话")

自定义元类控制类的实例化过程

自定义元类创建的类的对象实例化过程

例子

1class MyType(type): 23 def __init__(self,class_name,class_bases,class_dic): 4 print("类名",class_name) 5 print("基类",class_bases) 6 print("类字典",class_dic) 7 super().__init__(class_name,class_bases,class_dic) 89 def __call__(self, *args, **kwargs): 10 print("self----",self) 11 obj = object.__new__(self) #创建一个新的对象,这里就是创建People类的对象,一个实例化的过程 12 self.__init__(obj,*args,**kwargs) #执行对象的__init__方法,给对象属性字典加属性,这里的self是People类 13 return obj 1415 class People(object,metaclass=MyType): #这里metaclass = MyType就执行MyType("People",(ovject,),{}), 16 # 然后自动实例化调用MyType的__init__方法,生成一个people类 17 def __init__(self,name,age): 18 self.name = name 19 self.age = age 2021 def talk(self): 22 print("在说话。。。") 23 print("实例化之前") 24 p = People("nick",18) #这里触发了__call__方法,自己的类里没有call方法就找元类,执行元类的call方法 25 #执行call方法之后得到新的对象赋值给p, 26 print(p.__dict__) 27 print(p.name)

- 创建类时,先执行type的__init__。
- 类的实例化时,执行type的__call__,__call__方法的的返回值就是实例化的对象。

__call__内部调用:
- 类.__new__,创建对象
- 类.__init__,对象的初始化

单例模式

单例:即单个实例,指的是同一个类实例化多次的结果指向同一个对象,用于节省内存空间

单例模式主要是优化内存,无论你实例化多少次,始终用同一个对象

例子1

如果我们从配置文件中读取配置来进行实例化,在配置相同的情况下,就没必要重复产生对象浪费内存了

创建单例模式的方式1

1#用类实现单例模式 23 class MySQL(): 45 __instance = None #先定义一个空 67 def __init__(self): 8 self.host = "127.0.0.1" 9 self.port = "8090" 1011 @classmethod 12 def singleton(cls): 13 if not cls.__instance: #如果之前没有实例则第一次创建一个实例对象,之后如果有实例则直接返回该实例对象 14 obj = cls() 15 cls.__instance = obj #将第一次创建的对象赋值给类属性,方便下次调用 16 return cls.__instance 1718 obj1 = MySQL.singleton() #创建新的对象 19 obj2 = MySQL.singleton() 20 print(obj1 is obj2)

创建单例模式的方式2,用元类创建单例模式

1 #第二种方式,用元类实现单例模式 ​ 2 3class Mymeta(type): 45 def __init__(self,class_name,class_bases,class_dic): 6 super().__init__(class_name,class_bases,class_dic) 7 self.__instance = None #这里的self是根据这个元类创建的类,也可以把这个创建的类暂时理解为类的对象 89 def __call__(self, *args, **kwargs): 10 if not self.__instance: 11 obj = object.__new__(self) #用__new__方法创建类的对象 12 self.__init__(obj,*args, **kwargs) #运行类的__init__方法,为新建对象的属性字典赋值 13 self.__instance = obj #第一次创建对象时将类(self)的属性重新赋值为刚创建的obj 14 return self.__instance 151617 class Mysql(metaclass=Mymeta): 1819 def __init__(self): 20 self.host = "127.0.0.1" 21 self.port = 8090 2223 obj3 = Mysql() # 创建类的对象,调用元类的__call__方法 24 obj4 = Mysql() 25 print(obj3 is obj4)
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

Python3:sqlalchemy对mysql数据库操作,非sql语句

Python3:sqlalchemy对mysql数据库操作,非sql语句python3authorlizmdatetime2018020110:00:00coding:utf8'''