Python之路(第二十七篇) 面向对象进阶:内置方法、描述符

一、__call__

对象后面加括号,触发执行类下面的__call__方法。

创建对象时,对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()

  class Foo:
  ?
      def __call__(self, *args, **kwargs):
          print("我执行啦")
  ?
  f = Foo()
  f()  #对象加括号调用执行类下的__call__方法
  #输出结果 我执行啦

  

二、__next____iter__实现迭代器协议

迭代器协议是指:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代 (只能往后走不能往前退)

可迭代对象执行obj.__iter__()得到的结果就是迭代器对象。

在类中,如果有__iter____next__内置方法,那么就构成了迭代器。

例子

  
  class Foo:
  ?
      def __init__(self,n):
          self.n = n
  ?
      def __iter__(self):
          return self  #实例本身就是迭代对象,故返回自己
  ?
      def __next__(self):
          if self.n >10:
              raise StopIteration   #如果超过10就报StopIteration 错误
          self.n = self.n + 1
          return self.n
  ?
  f = Foo(7)
  for i in f:  #for循环自动调用__next__方法,实现了迭代取值
      print(i)

  

例子2

输出100内的斐契那波数列

  
  class F:
  ?
      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
          if self.a > 100:
              raise StopIteration
          return self.a
  ?
  f = F()
  for i in f:
      print(i)

  

三、描述符(__get__,__set__,__delete__)

描述符(descriptor):

1、描述符本质

就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议。__get__():调用一个属性时,触发__set__():为一个属性赋值时,触发__delete__():采用del删除属性时,触发

2、描述符的作用

是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)

描述符是在另外一个类的类属性进行定义的,描述符在一个类的类属性__dict__字典里

例子1

  class Foo:
  ?
      def __get__(self, instance, owner):
          print("执行了__get__")
  ?
      def __set__(self, instance, value):
          print("执行了__set__")
  ?
      def __delete__(self, instance):
          print("执行了__delete__")
  ?
  ?
  class Bar:
      x = Foo()
  ?
      def __init__(self,name):
          self.name = name
  ?
  ?
  b = Bar("nick")
  b.x     #调用执行描述符里的__get__方法
  print(b.x)  #
  b.x = 1  # 调用执行描述符里的__set__方法
  print(b.__dict__)
  del b.x  #调用执行描述符里的__delete__方法
  print(b.__dict__)

  

输出结果

  执行了__get__
  执行了__get__
  None
  执行了__set__
  {‘name‘: ‘nick‘}
  执行了__delete__
  {‘name‘: ‘nick‘}

  

例子2

  #描述符Str
  class Str:
      def __get__(self, instance, owner):
          print(‘Str调用‘)
      def __set__(self, instance, value):
          print(‘Str设置...‘)
      def __delete__(self, instance):
          print(‘Str删除...‘)
  ?
  #描述符Int
  class Int:
      def __get__(self, instance, owner):
          print(‘Int调用‘)
      def __set__(self, instance, value):
          print(‘Int设置...‘)
      def __delete__(self, instance):
          print(‘Int删除...‘)
  ?
  class People:
      name=Str()
      age=Int()
      def __init__(self,name,age): #name被Str类代理,age被Int类代理,
          self.name=name
          self.age=age
  ?
  #何地?:定义成另外一个类的类属性
  ?
  #何时?:且看下列演示
  ?
  p1=People(‘alex‘,18)
  ?
  #描述符Str的使用
  p1.name
  p1.name=‘egon‘
  del p1.name
  ?
  #描述符Int的使用
  p1.age
  p1.age=18
  del p1.age
  ?
  #我们来瞅瞅到底发生了什么
  print("__p1.__dict__",p1.__dict__)
  print(People.__dict__)
  ?
  #补充
  print(type(p1) == People) #type(obj)其实是查看obj是由哪个类实例化来的
  print(type(p1).__dict__ == People.__dict__)

  

输出结果

  Str设置...
  Int设置...
  Str调用
  Str设置...
  Str删除...
  Int调用
  Int设置...
  Int删除...
  __p1.__dict__ {}
  {‘__module__‘: ‘__main__‘, ‘name‘: <__main__.Str object at 0x021C6850>, ‘age‘: <__main__.Int object at 0x021C6870>, ‘__init__‘: <function People.__init__ at 0x021C5DB0>, ‘__dict__‘: <attribute ‘__dict__‘ of ‘People‘ objects>, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘People‘ objects>, ‘__doc__‘: None}
  True
  True

  

3、描述符分两种

(1) 数据描述符:至少实现了__get__()__set__()

  ?
  class Foo:
      def __set__(self, instance, value):
          print(‘set‘)
      def __get__(self, instance, owner):
          print(‘get‘)

  

(2) 非数据描述符:没有实现__set__()

  class Foo:
      def __get__(self, instance, owner):
          print(‘get‘)

  

注意:非数据描述符一般是只有__get__,如果保留__delete__执行会报错。

4、 注意事项:

(1)描述符本身应该定义成新式类,被代理的类也应该是新式类(python3中全部是新式类)

(2)必须把描述符定义成另外一个类的类属性,不能为定义到构造函数中,

(3)要严格遵循该优先级,优先级由高到底分别是

a.类属性b.数据描述符c.实例属性d.非数据描述符e.找不到的属性触发__getattr__()

例子1

  
  class Foo:
  ?
      def __get__(self, instance, owner):
          print("执行了__get__")
  ?
      def __set__(self, instance, value):
          print("执行了__set__")
  ?
      def __delete__(self, instance):
          print("执行了__delete__")
  ?
  class People:
  ?
      name = Foo()
  ?
      def __init__(self,name):
          self.name = name
  ?
  ?
  p = People("nick")
  People.name  = "nick"  #调用执行了描述符的__set__方法,这一步类属性由之前的描述符被定义成另外一个字符串,
  # 所以下面再次调用就无法再次使用描述符了
  People.name
  ?
  #可以得出结论,类属性的优先级大于数据描述符

  

例子2

  
  class Foo:
  ?
      def __get__(self, instance, owner):
          print("执行了__get__")
  ?
      def __set__(self, instance, value):
          print("执行了__set__")
  ?
      def __delete__(self, instance):
          print("执行了__delete__")
  ?
  class People:
  ?
      name = Foo()
  ?
      def __init__(self,name):
          self.name = name
  ?
  ?
  p = People("nick")  #实例化对象,调用数据描述符的__set__,
  # 但是由于描述符的__set__只是执行了打印操作,什么都没做,所以p对象的__dict__什么都没有
  p.name = "nicholas"
  print(p.__dict__) #输出的结果为空
  ?
  #因此可以得出结论,数据描述符的优先级大于实例属性(字典操作)

  

例子3

  class Foo(object):
      def __init__(self):
          pass
  ?
      def __get__(self, instance, owner):
          print("执行了__get__")
  ?
  class People(object):
  ?
      name = Foo("x")
  ?
      def __init__(self,name,age):
          self.name = name
          self.age = age
  ?
  ?
  ?
  p = People("nick",18)  #实例化对象,这里由于是非数据描述符,优先级低于实例属性,
  # 所以这里直接设置了实例属性,而不再调用描述符
  print(p.name)  #打印直接输出实例属性
  print(p.__dict__)
  #输出的结果:{‘name‘: ‘nick‘, ‘age‘: 18}
  ?
  #因此可以得出结论,实例属性的优先级大于非数据描述符

  

例子4

  class Foo(object):
      def __init__(self,name2):
          self.name2 = name2
  ?
      def __get__(self, instance, owner):
          print("执行了__get__")
  ?
  ?
  class People(object):
  ?
      name = Foo("x")
  ?
      def __init__(self,name,age):
          self.name = name
          self.age = age
  ?
      def __getattr__(self, item):
          print("__getattr__")
  ?
  ?
  p = People("nick",18)  #实例化对象,这里由于是非数据描述符,优先级低于实例属性,
  # 所以这里直接设置了实例属性,而不再调用描述符
  print(p.name)
  print(p.sex)  #调用不存在的属性执行了__getattr__
  print(p.__dict__)
  #输出的结果:{‘name‘: ‘nick‘, ‘age‘: 18}

  

5、描述符的应用

例子1

  
  class Type:
  ?
      def __init__(self,key,expect_type):
          self.key = key
          self.expect_type = expect_type
  ?
      def __get__(self, instance, owner):
          print("执行__get__方法")
          print(self)                #这里的self就是type类的对象
          print(instance)            #这里的instance就是传入的People类的对象
          print("执行__get__方法")
          return  instance.__dict__[self.key]   #通过instance的字典获取对象的属性值
  ?
      def __set__(self, instance, value):
          print("执行__set__方法")
          instance.__dict__[self.key] = value   #instance是另一个类的对象,这里要设置对象的属性字典
  ?
      def __delete__(self, instance):
          print("执行__delete__方法")
          instance.__dict__.pop(self.key)  #删除对象的属性
  ?
  class People:
      name = Type("name",str)
      age = Type("age",int)
  ?
      def __init__(self,name,age):
          self.name = name
          self.age = age
  ?
  p1 = People("nick",18)  #调用2次描述符,对对象的字典进行设置
  print(p1.name)  #通过数据描述符获取对象的属性值
  print(p1.__dict__)
  p1.age = 20  #调用描述符对对象进行设置
  print(p1.__dict__)

  

输出结果

  
  执行__set__方法
  执行__set__方法
  执行__get__方法
  <__main__.Type object at 0x004CB4F0>
  <__main__.People object at 0x02106DF0>
  执行__get__方法
  nick
  {‘name‘: ‘nick‘, ‘age‘: 18}
  执行__set__方法
  {‘name‘: ‘nick‘, ‘age‘: 20}

  

  ?

例子2

  
  class Type:
  ?
      def __init__(self,key,expect_type):
          self.key = key
          self.expect_type = expect_type
  ?
      def __get__(self, instance, owner):
          print("执行__get__方法")
          print(self)                #这里的self就是type类的对象
          print(instance)            #这里的instance就是传入的People类的对象
          print("执行__get__方法")
          return  instance.__dict__[self.key]   #通过instance的字典获取对象的属性值
  ?
      def __set__(self, instance, value):
          print("执行__set__方法")
          if not isinstance(value,self.expect_type):
              print("您输入的%s不是%s"%(self.key,self.expect_type))
              raise TypeError
          instance.__dict__[self.key] = value   #instance是另一个类的对象,这里要设置对象的属性字典
  ?
      def __delete__(self, instance):
          print("执行__delete__方法")
          instance.__dict__.pop(self.key)  #删除对象的属性
  ?
  class People:
      name = Type("name",str)
      age = Type("age",int)
  ?
      def __init__(self,name,age):
          self.name = name
          self.age = age
  ?
  p1 = People("nick",18)  #调用2次描述符,对对象的字典进行设置
  print(p1.name)  #通过数据描述符获取对象的属性值
  print(p1.__dict__)
  p1.age = 20  #调用描述符对对象进行设置
  print(p1.__dict__)
  # p1.name = 11  #通过描述符的if not isinstance(value,self.expect_type)判断属性的类型
  ?
  # p2 = People(88,18)  #通过描述符的if not isinstance(value,self.expect_type)判断属性的类型

  

四、__enter____exit__

打开文件操作用 with open() as f操作,这叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter____exit__方法。

__enter__(self):当with开始运行的时候触发此方法的运行

__exit__(self, exc_type, exc_val, exc_tb):当with运行结束之后触发此方法的运行

exc_type如果抛出异常,这里获取异常的类型

exc_val如果抛出异常,这里显示异常内容

exc_tb如果抛出异常,这里显示所在位置

用途或者说好处:

1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预

2.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处

例子

class OPEN:

    def __init__(self,name):
        self.name = name

    def __enter__(self):
        print("执行__enter__")
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print("执行__exit__")
        print(exc_type)
        print(exc_val)
        print(exc_tb)
        print("执行__exit__2222")

with OPEN("a.txt") as f:
    print(f)  #执行打印__enter__内置方法,同时打印内置方法返回的结果

#with 语句结束时执行__exit__方法,没有错误则打印None,有错误则打印错误的信息
print("上下文管理协议")

  

原文地址:https://www.cnblogs.com/Nicholas0707/p/9458021.html

时间: 2024-10-18 04:55:16

Python之路(第二十七篇) 面向对象进阶:内置方法、描述符的相关文章

面向对象进阶——内置方法(二)

七.__setitem__, __getitem__, __delitem__ item系列 class Foo: def __init__(self, name): self.name = name def __getitem__(self, item): # print('getitem...') #print(item) return self.__dict__.get(item) # 字典get方法有则取值,无也不会报错 def __setitem__(self, key, value)

4-19 面向对象 的内置方法

1,__str__ 和 __repr__ __str__就是改变对象的字符串显示. Python有一个内置的函数叫repr,它能把一个对象用字符串的形式表达出来以便辨认,这就是"字符串表示形式".repr就是通过__repr__这个特殊方法来得到一个对象的字符串表示形式.如果没有实现__repr__,当我们再控制台里打印一个变量的实例时,得到的字符串可能会是<__main__.Object at 0x14af07dbe80>,这个表示的是打印的对象,以及对象的内存地址. s

Python之路(第十七篇)logging模块

一.logging模块 (一).日志相关概念 日志是一种可以追踪某些软件运行时所发生事件的方法.软件开发人员可以向他们的代码中调用日志记录相关的方法来表明发生了某些事情.一个事件可以用一个可包含可选变量数据的消息来描述.此外,事件也有重要性的概念,这个重要性也可以被称为严重性级别(level). 1.日志的作用 通过log的分析,可以方便用户了解系统或软件.应用的运行情况:如果你的应用log足够丰富,也可以分析以往用户的操作行为.类型喜好.地域分布或其他更多信息:如果一个应用的log同时也分了多

Python之路(第二十篇) subprocess模块

一.subprocess模块 subprocess英文意思:子进程 那什么是进程呢? (一)关于进程的相关理论基础知识 进程是对正在运行程序的一个抽象,进程的概念起源于操作系统,是操作系统最核心的概念,操作系统的其他所有内容都是围绕进程的概念展开的. 所以想要真正了解进程,必须事先了解操作系统. 程序员无法把所有的硬件操作细节都了解到,管理这些硬件并且加以优化使用是非常繁琐的工作,这个繁琐的工作就是操作系统来干的,有了他,程序员就从这些繁琐的工作中解脱了出来,只需要考虑自己的应用软件的编写就可以

面向对象进阶------&gt;内置函数 str repr new

__new__方法: 我们来讲个非常非常重要的内置函数和init一样重要__new__其实在实例话对象的开始  是先继承父类中的new方法再执行init的  就好比你生孩子 先要把孩子生出来才能对孩子穿衣服的  new就是生孩子 init是给孩子穿衣服 new()是在新式类中新出现的方法,它作用在构造方法init()建造实例之前,可以这么理解,在Python 中存在于类里面的构造方法init()负责将类的实例化,而在init()调用之前,new()决定是否要使用该init()方法,因为new()

python面向对象常用内置方法

class Person(): #构造函数 def __init__(self,name,age=None): self.name=name self.age=age self.lists=[1,3,5] self.dicts={} #打印函数,类似java中的toSring() def __str__(self): return 'Peason(%s)'%self.name #对象的字典表示 def __dict__(self): return {'name':self.name} #对象相加

Python之路(第二十六篇) 面向对象进阶:内置方法

一.__getattribute__ object.__getattribute__(self, name) 无条件被调用,通过实例访问属性.如果class中定义了__getattr__(),则__getattr__()不会被调用(除非显示调用或引发AttributeError异常) class Foo: ? def __init__(self,x): self.x = x ? def __getattr__(self, item): print("执行__getattr__") ?

python全栈开发【第十七篇】面向对象反射和内置方法

一.静态方法(staticmethod)和类方法(classmethod) 类方法:有个默认参数cls,并且可以直接用类名去调用,可以与类属性交互(也就是可以使用类属性) 静态方法:让类里的方法直接被类调用,就像正常调用函数一样 类方法和静态方法的相同点:都可以直接被类调用,不需要实例化 类方法和静态方法的不同点: 类方法必须有一个cls参数表示这个类,可以使用类属性 静态方法不需要参数 绑定方法:分为普通方法和类方法 普通方法:默认有一个self对象传进来,并且只能被对象调用-------绑定

Python之路【第九篇】:Python操作 RabbitMQ、Redis、Memcache、SQLAlchemy

Python之路[第九篇]:Python操作 RabbitMQ.Redis.Memcache.SQLAlchemy Memcached Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站的速度.Memcached基于一个存储键/值对的hashmap.其守护进程(daemon )是用C写的,但是客户端可以用任何语言来编写,并通过memcached协议与守护进程通信. Memc