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

一、类的装饰器

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

例子

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

  

 

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

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

  

 

 

类的装饰器应用

例子

  class Type:
  ​
      def __init__(self,key,except_type):  #People对象的key,和期望的数据类型
          self.key = key
          self.except_type = except_type
  ​
      def __get__(self, instance, owner):
          return isinstance.__dict__[self.key]
  ​
      def __set__(self, instance, value):
          print("instance---",instance)
          if not isinstance(value,self.except_type):
              print("您输入的类型不是%s"%self.except_type)
              raise TypeError
          instance.__dict__[self.key] = value
  ​
      def __delete__(self, instance):
          isinstance.__dict__.pop(self.key)
  ​
  def deco(**kwargs):
      def wrapper(obj):      #类的装饰器
          for key,val in kwargs.items():
              setattr(obj,key,Type(key,val))  #设置people类对象的每个参数的描述符
          return obj
      return wrapper
  ​
  @deco(name=str,age=int)
  class People:
  ​
      def __init__(self,name,age):
          self.name = name
          self.age = age
  ​
  ​
  p = People("nick",18)
  print(p.__dict__)

  

 

 

二、自定义property

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

例子

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

  

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

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

  

 

三、property补充

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

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

  class People:
  ​
      def __init__(self):
          self.study = "8h"
  ​
      @property
      def study(self):
          print("获取study,执行描述符的__get__方法")
          return self.val
          # return self.study  #无线递归
  ​
      @study.setter
      def study(self,value):
          print("执行__set__方法")
          self.val = value
  ​
      @study.deleter
      def study(self):
          print("执行__delete__方法")
          del self.val
  ​
  p = People()
  print(p.study)  #获取对象的study属性 ,self.study实际上是存在self.val里
  p.study = "10h"  #执行property描述符的__set__方法,设置对象的属性,
  print(p.__dict__)
  del p.study  #执行property描述符的__delete_方法,删除对象的属性
  print(p.__dict__)

  

 

四、元类

 

exec()函数

exec:三个参数

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

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

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

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

例子

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

  

定义类的两种方式

(1)用class关键字定义类

  
定义类的方式一:用class关键字定义

  class People:
  ​
      country = "china"
      def __init__(self,name):
          self.name = name
  ​
      def talk(self):
          print("在说话")

  

 

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

准备工作:

创建类主要分为三部分

  a 类名

  b 类的父类

  c 类体

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

  

 

什么是元类?

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

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

元类的参数

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

1、类名class_name="xxx"

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

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

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

例子

  
  class Foo: #一般的用class 关键字创建的类
      pass
  ​
  f = Foo()
  print(Foo)
  print(Foo.__dict__)
  ​
  t = type("FFo",(object,),{})  #由元类创造出来的一般类
  print(t)
  print(t.__dict__)

  

输出结果

  
  <class '__main__.Foo'>
  {'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None}
  <class '__main__.FFo'>
  {'__module__': '__main__', '__dict__': <attribute '__dict__' of 'FFo' objects>, '__weakref__': <attribute '__weakref__' of 'FFo' objects>, '__doc__': None}
  ​

  

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

例子2

  
  class Foo:
      x = 1
      def __init__(self,name):
          self.name = name
  ​
  ​
  f = Foo("nick")
  print(Foo)
  print(Foo.__dict__)
  ​
  ​
  def __init(self,name):
      self.name = name
  ​
  t = type("FFo",(object,),{"__init__":__init,"x":1}) #可以直接在属性字典里为创建的类添加属性和方法
  print(t)
  print(t.__dict__)
  t=T("nick")
  print(t.__dict__)

  

输出结果

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

  

自定义元类

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

例子1

  
  class MyType(type):
  ​
      def __init__(self,class_name,class_bases,class_dic):
          print("类名",class_name)
          print("基类",class_bases)
          print("类字典",class_dic)
          super().__init__(class_name,class_bases,class_dic)
  ​
  ​
  class People(object,metaclass=MyType):  #这里metaclass = MyType就执行MyType("People",(ovject,),{}),
      # 然后自动实例化调用MyType的__init__方法
      def __init__(self,name,age):
          self.name = name
          self.age = age
  ​
      def talk(self):
          print("在说话。。。")
  ​
  p = People("nick",18)
  print(p.__dict__)

  

输出结果

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

  

 

自定义元类控制类的创建

例子1

对新建的类名的要求

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

  

例子2

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

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

  

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

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

 

例子

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

  

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

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

 

单例模式

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

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

 

例子1

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

创建单例模式的方式1

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

  

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

  
#第二种方式,用元类实现单例模式

  class Mymeta(type):
  ​
      def __init__(self,class_name,class_bases,class_dic):
          super().__init__(class_name,class_bases,class_dic)
          self.__instance = None  #这里的self是根据这个元类创建的类,也可以把这个创建的类暂时理解为类的对象
  ​
      def __call__(self, *args, **kwargs):
          if not self.__instance:
              obj = object.__new__(self)   #用__new__方法创建类的对象
              self.__init__(obj,*args, **kwargs)  #运行类的__init__方法,为新建对象的属性字典赋值
              self.__instance = obj   #第一次创建对象时将类(self)的属性重新赋值为刚创建的obj
          return self.__instance
  ​
  ​
  class Mysql(metaclass=Mymeta):
  ​
      def __init__(self):
          self.host = "127.0.0.1"
          self.port = 8090
  ​
  obj3 = Mysql()  # 创建类的对象,调用元类的__call__方法
  obj4 = Mysql()
  print(obj3 is obj4)
 

  

 

 

 

posted on 2018-08-26 17:37  Nicholas--  阅读(605)  评论(0编辑  收藏  举报

导航