实现迭代器
简单示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
   | class Foo:     def __init__(self, x):         self.x = x
      def __iter__(self):         return self
      def __next__(self):         self.x += 1         return self.x
 
  f = Foo(3) for i in f:     print(i)
   | 
 
StopIteration异常版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
   | class Foo:     def __init__(self, start, stop):         self.num = start         self.stop = stop
      def __iter__(self):         return self
      def __next__(self):         if self.num >= self.stop:             raise StopIteration         n = self.num         self.num += 1         return n
 
  f = Foo(1, 5) from collections import Iterable, Iterator print(isinstance(f, Iterator)) True for i in Foo(1, 5):     print(i)
  ''' 1 2 3 4 '''
   | 
 
模拟range
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
   | class Range:     def __init__(self, n, stop, step):         self.n = n         self.stop = stop         self.step = step
      def __next__(self):         if self.n >= self.stop:             raise StopIteration         x = self.n         self.n += self.step         return x
      def __iter__(self):         return self
 
  for i in Range(1, 7, 3):     print(i)      ''' 1 4 '''
   | 
 
斐波那契数列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
   | class Fib:     def __init__(self):         self._a = 0         self._b = 1
      def __iter__(self):         return self
      def __next__(self):         self._a, self._b = self._b, self._a + self._b         return self._a
 
  f1 = Fib() for i in f1:     if i > 100:         break     print('%s ' % i, end='')  
   | 
 
实现文件上下文管理
1 2
   | with open('a.txt') as f:     '代码块'
  | 
 
- 上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明enter和exit方法
 
上下文管理协议
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
   | class Open:     def __init__(self, name):         self.name = name
      def __enter__(self):         print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')              def __exit__(self, exc_type, exc_val, exc_tb):         print('with中代码块执行完毕时执行我啊')
 
  with Open('a.txt') as f:     print('=====>执行代码块')      ''' 出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量 =====>执行代码块 with中代码块执行完毕时执行我啊 '''
   | 
 
- exit()中的三个参数分别代表异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都无法执行
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
   | class Open:     def __init__(self, name):         self.name = name
      def __enter__(self):         print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
      def __exit__(self, exc_type, exc_val, exc_tb):         print('with中代码块执行完毕时执行我啊')         print(exc_type)         print(exc_val)         print(exc_tb)
 
  try:     with Open('a.txt') as f:         print('=====>执行代码块')         raise AttributeError('***着火啦,救火啊***') except Exception as e:     print(e)      ''' 出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量 =====>执行代码块 with中代码块执行完毕时执行我啊 <class 'AttributeError'> ***着火啦,救火啊*** <traceback object at 0x1065f1f88> ***着火啦,救火啊*** '''
   | 
 
- 如果__exit()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
   | class Open:     def __init__(self, name):         self.name = name
      def __enter__(self):         print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
      def __exit__(self, exc_type, exc_val, exc_tb):         print('with中代码块执行完毕时执行我啊')         print(exc_type)         print(exc_val)         print(exc_tb)         return True
 
  with Open('a.txt') as f:     print('=====>执行代码块')     raise AttributeError('***着火啦,救火啊***') print('0' * 100)  
  ''' 出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量 =====>执行代码块 with中代码块执行完毕时执行我啊 <class 'AttributeError'> ***着火啦,救火啊*** <traceback object at 0x1062ab048> 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 '''
   | 
 
模拟open
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
   | class Open:     def __init__(self, filepath, mode='r', encoding='utf-8'):         self.filepath = filepath         self.mode = mode         self.encoding = encoding
      def __enter__(self):                  self.f = open(self.filepath, mode=self.mode, encoding=self.encoding)         return self.f
      def __exit__(self, exc_type, exc_val, exc_tb):                  self.f.close()         return True
      def __getattr__(self, item):         return getattr(self.f, item)
 
  with Open('a.txt', 'w') as f:     print(f)     f.write('aaaaaa')     f.wasdf        '''     <_io.TextIOWrapper name='a.txt' mode='w' encoding='utf-8'> '''
   | 
 
优点
- 使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预
 
- 在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在exit中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处
 
什么是元类
- 在python中一切皆对象,那么我们用class关键字定义的类本身也是一个对象,负责产生该对象的类称之为元类,即元类可以简称为类的类
 
1 2
   | class Foo:  # Foo=元类()     pass
   | 
 

为什么用元类
- 元类是负责产生类的,所以我们学习元类或者自定义元类的目的:是为了控制类的产生过程,还可以控制对象的产生过程
 
内置函数exec(储备)
1 2 3 4 5 6 7 8 9 10 11 12
   | cmd = """ x=1 print('exec函数运行了') def func(self):     pass """ class_dic = {}
 
  exec(cmd, {}, class_dic)  
  print(class_dic)  
   | 
 
class创建类
- 如果说类也是对象,那么用class关键字的去创建类的过程也是一个实例化的过程,该实例化的目的是为了得到一个类,调用的是元类
 
- 用class关键字创建一个类,用的默认的元类type,因此以前说不要用type作为类别判断
 
1 2 3 4 5 6 7 8 9 10 11 12
   | class People:       country = 'China'
      def __init__(self, name, age):         self.name = name         self.age = age
      def eat(self):         print('%s is eating' % self.name)                   print(type(People))  
   | 
 

5.1 type实现
- 创建类的3个要素:类名,基类,类的名称空间
 
- People = type(类名,基类,类的名称空间)
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
   | class_name = 'People'  
  class_bases = (object, )  
 
  class_dic = {} class_body = """ country='China' def __init__(self,name,age):     self.name=name     self.age=age def eat(self):     print('%s is eating' %self.name) """
  exec(     class_body,     {},     class_dic, )
 
  print(class_name)  
  print(class_bases)  
  print(class_dic)   ''' {'country': 'China', '__init__': <function __init__ at 0x10a0bc048>, 'eat': <function eat at 0x10a0bcd08>} '''
   | 
 
- People = type(类名,基类,类的名称空间)
 
1 2 3 4 5
   | People1 = type(class_name, class_bases, class_dic) print(People1)  
  obj1 = People1(1, 2) obj1.eat()  
   | 
 
1 2 3 4
   | print(People)  
  obj = People1(1, 2) obj.eat()  
   | 
 
自定义元类控制类的创建
1 2 3 4 5 6 7 8
   | class Mymeta(type):  # 只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类     def __init__(self, class_name, class_bases, class_dic):         print('self:', self)  # 现在是People         print('class_name:', class_name)         print('class_bases:', class_bases)         print('class_dic:', class_dic)         super(Mymeta, self).__init__(class_name, class_bases,                                      class_dic)  # 重用父类type的功能
   | 
 
- 分析用class自定义类的运行原理(而非元类的的运行原理):
- 拿到一个字符串格式的类名class_name=’People’
 
- 拿到一个类的基类们class_bases=(obejct,)
 
- 执行类体代码,拿到一个类的名称空间class_dic={…}
 
- 调用People=type(class_name,class_bases,class_dic)
 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
   | class People(object, metaclass=Mymeta):       country = 'China'
      def __init__(self, name, age):         self.name = name         self.age = age
      def eat(self):         print('%s is eating' % self.name)                   '''         self: <class '__main__.People'> class_name: People class_bases: (<class 'object'>,) class_dic: {'__module__': '__main__', '__qualname__': 'People', 'country': 'China', '__init__': <function People.__init__ at 0x10a0bcbf8>, 'eat': <function People.eat at 0x10a0bc2f0>} '''
   | 
 
应用
- 自定义元类控制类的产生过程,类的产生过程其实就是元类的调用过程
 
- 我们可以控制类必须有文档,可以使用如下的方式实现
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
   | class Mymeta(type):       def __init__(self, class_name, class_bases, class_dic):         if class_dic.get('__doc__') is None or len(                 class_dic.get('__doc__').strip()) == 0:             raise TypeError('类中必须有文档注释,并且文档注释不能为空')         if not class_name.istitle():             raise TypeError('类名首字母必须大写')         super(Mymeta, self).__init__(class_name, class_bases,                                      class_dic)                     try:
      class People(object, metaclass=Mymeta                  ):                    country = 'China'
          def __init__(self, name, age):             self.name = name             self.age = age
          def eat(self):             print('%s is eating' % self.name) except Exception as e:     print(e)      """类中必须有文档注释,并且文档注释不能为空"""
   | 
 
call(储备)
- 要想让obj这个对象变成一个可调用的对象,需要在该对象的类中定义一个方法、、call方法,该方法会在调用对象时自动触发
 
1 2 3 4 5 6 7 8 9 10 11 12
   | class Foo:     def __call__(self, *args, **kwargs):         print(args)         print(kwargs)         print('__call__实现了,实例化对象可以加括号调用了')
 
  obj = Foo() obj('lqz', age=18) ('lqz',) {'age': 18} __call__实现了,实例化对象可以加括号调用了
   | 
 
new(储备)
我们之前说类实例化第一个调用的是init,但init其实不是实例化一个类的时候第一个被调用 的方法。当使用 Persion(name, age) 这样的表达式来实例化一个类时,最先被调用的方法 其实是 new 方法。
new方法接受的参数虽然也是和init一样,但init是在类实例创建之后调用,而 new方法正是创建这个类实例的方法。
注意:*new*() 函数只能用于从object继承的新式类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
   | class A:     pass
 
  class B(A):     def __new__(cls):         print("__new__方法被执行")         return cls.__new__(cls)
      def __init__(self):         print("__init__方法被执行")
 
  b = B()
   | 
 
自定义元类控制类的实例化
1 2 3 4 5 6 7 8 9 10 11 12 13
   | class Mymeta(type):     def __call__(self, *args, **kwargs):         print(self)           print(args)           print(kwargs)                                      obj = self.__new__(self)                    self.__init__(obj, *args, **kwargs)                  return obj
   | 
 
- People = Mymeta(),People()则会触发call
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
   | class People(object, metaclass=Mymeta):     country = 'China'
      def __init__(self, name, age):         self.name = name         self.age = age
      def eat(self):         print('%s is eating' % self.name)
 
 
 
 
 
 
 
 
   | 
 
- 类的调用,即类实例化就是元类的调用过程,可以通过元类Mymeta的call方法控制
 
- 分析:调用Pepole的目的
- 先造出一个People的空对象
 
- 为该对空对象初始化独有的属性
 
- 返回一个初始化好的对象
 
 
1 2 3 4 5 6 7 8
   | obj = People('lqz', age=18) ''' <class '__main__.People'> ('lqz',) {'age': 18} '''
  print(obj.__dict__)  
  | 
 
自定义元类后类的继承顺序
结合python继承的实现原理+元类重新看属性的查找应该是什么样子呢???
在学习完元类后,其实我们用class自定义的类也全都是对象(包括object类本身也是元类type的 一个实例,可以用type(object)查看),我们学习过继承的实现原理,如果把类当成对象去看,将下述继承应该说成是:对象OldboyTeacher继承对象Foo,对象Foo继承对象Bar,对象Bar继承对象object
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
   | class Mymeta(type):       n = 444
      def __call__(self, *args,                  **kwargs):           obj = self.__new__(self)         self.__init__(obj, *args, **kwargs)         return obj
 
  class Bar(object):     n = 333
 
  class Foo(Bar):     n = 222
 
  class OldboyTeacher(Foo, metaclass=Mymeta):     n = 111
      school = 'oldboy'
      def __init__(self, name, age):         self.name = name         self.age = age
      def say(self):         print('%s says welcome to the oldboy to learn Python' % self.name)
 
  print(OldboyTeacher.n)  
 
  print(OldboyTeacher.n)  
   | 
 
- 查找顺序:
- 先对象层:OldoyTeacher->Foo->Bar->object
 
- 然后元类层:Mymeta->type
 
 
依据上述总结,我们来分析下元类Mymeta中call里的self.new的查找
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
   | class Mymeta(type):     n = 444
      def __call__(self, *args,                  **kwargs):           obj = self.__new__(self)         print(self.__new__ is object.__new__)  
 
  class Bar(object):     n = 333
           
 
  class Foo(Bar):     n = 222
           
 
  class OldboyTeacher(Foo, metaclass=Mymeta):     n = 111
      school = 'oldboy'
      def __init__(self, name, age):         self.name = name         self.age = age
      def say(self):         print('%s says welcome to the oldboy to learn Python' % self.name)
           
 
  OldboyTeacher('lqz',               18)  
   | 
 
总结,Mymeta下的call里的self.new在OldboyTeacher、Foo、Bar里都没有找到new的情况下,会去找object里的new,而object下默认就有一个new,所以即便是之前的类均未实现new,也一定会在object中找到一个,根本不会、也根本没必要再去找元类Mymeta->type中查找new
练习
需求:使用元类修改属性为隐藏属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
   | class Mymeta(type):     def __init__(self, class_name, class_bases, class_dic):                  super(Mymeta, self).__init__(class_name, class_bases, class_dic)
      def __call__(self, *args, **kwargs):                  obj = self.__new__(self)         self.__init__(obj, *args, **kwargs)                  obj.__dict__ = {             '_%s__%s' % (self.__name__, k): v             for k, v in obj.__dict__.items()         }
          return obj               class Foo(object, metaclass=Mymeta):       def __init__(self, name, age, sex):         self.name = name         self.age = age         self.sex = sex
 
  obj = Foo('lqz', 18, 'male')
 
  print(obj.__dict__)  
   |