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

时间:2021-07-07 22:32:13

一、__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("上下文管理协议")

  

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

  1. Python之路&lpar;第二十三篇&rpar; 面向对象初级:静态属性、静态方法、类方法

    一.静态属性 静态属性相当于数据属性. 用@property语法糖装饰器将类的函数属性变成可以不用加括号直接的类似数据属性. 可以封装逻辑,让用户感觉是在调用一个普通的数据属性. 例子 class R ...

  2. python 面向对象进阶之内置方法

    一 isinstance(obj,cls)和issubclass(sub,super) 1.1,isinstance(obj,cls)检查是否obj是否是类 cls 的对象 class Foo(obj ...

  3. python成长之路第三篇&lpar;3&rpar;&lowbar;内置函数及生成器迭代器

    打个广告欢迎加入linux,python资源分享群群号:478616847 目录: 1.lambda表达式 2.map内置函数 3.filter内置函数 4.reduce内置函数 5.yield生成器 ...

  4. 面向对象进阶------&gt&semi;内置函数 str repr new call 方法

    __new__方法: 我们来讲个非常非常重要的内置函数和init一样重要__new__其实在实例话对象的开始  是先继承父类中的new方法再执行init的  就好比你生孩子 先要把孩子生出来才能对孩子 ...

  5. Python—面向对象06 内置方法

    一 .isinstance(obj,cls)和issubclass(sub,super) isinstance(obj,cls)检查是否obj是否是类 cls 的对象 class Foo(object ...

  6. python day21 ——面向对像-反射 getattr,内置方法

    一.反射:用字符串数据类型的变量名来访问这个变量的值 上代码^_^ # class Student: # ROLE = 'STUDENT' # @classmethod # def check_cou ...

  7. Python之路&lpar;第十七篇&rpar;logging模块

    一.logging模块 (一).日志相关概念 日志是一种可以追踪某些软件运行时所发生事件的方法.软件开发人员可以向他们的代码中调用日志记录相关的方法来表明发生了某些事情.一个事件可以用一个可包含可选变 ...

  8. Python之路&lpar;第二十篇&rpar; subprocess模块

    一.subprocess模块 subprocess英文意思:子进程 那什么是进程呢? (一)关于进程的相关理论基础知识 进程是对正在运行程序的一个抽象,进程的概念起源于操作系统,是操作系统最核心的概念 ...

  9. python3-面向对象进阶&lpar;内置方法&rpar;

    面向对象进阶: isinstance和issubclass 反射 __setattr__,__getattr,__delattr__ __setitem__,__getitem,__delitem__ ...

随机推荐

  1. python的断言

    assert的语法格式: assert expression 它的等价语句为: if not expression: raise AssertionError 这段代码用来检测数据类型的断言,因为 a ...

  2. Android发送短信核心代码

    核心代码:(1)SmsManager manager = SmsManager.getDefault(); //获得默认的消息管理器(2)ArrayList<String> list = ...

  3. 【BZOJ1005】【HNOI2008】明明的烦恼

    又是看黄学长的代码写的,估计我的整个BZOJ平推计划都要看黄学长的代码写 原题: 自从明明学了树的结构,就对奇怪的树产生了兴趣......给出标号为1到N的点,以及某些点最终的度数,允许在任意两点间连 ...

  4. Linux下的多进程编程

    1.进程 1.1进程的定义 <计算机操作系统>这门课对进程有这样的描述:进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统 ...

  5. css边框阴影问题

    阴影落在下方:box-shadow: 0 3px 5px rgba(0, 0, 0, .2); 阴影落在四周:box-shadow: 0 3px 5px rgba(0, 0, 0, .2), 0 0 ...

  6. &lbrack;Hive - LanguageManual&rsqb; Sampling

    Sampling Syntax Sampling Bucketized Table Block Sampling Sampling Syntax  抽样语法 Sampling Bucketized T ...

  7. 按ctrl &plus; c 播放下一曲音乐

    ./a.out . #include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<dirent. ...

  8. 关于MyEclipse启动报错&colon;Error starting static Resources&semi;下面伴随Failed to start component &lbrack;StandardServer&lbrack;8005&rsqb;&rsqb;&semi; A child container failed during start&period;的错误提示解决办法&period;

    最后才发现原因是Tomcat的server.xml配置文件有问题:apache-tomcat-7.0.67\conf的service.xml下边多了类似与 <Host appBase=&quot ...

  9. redgate的mysql架构比较和数据比较工具

    redgate的mysql架构比较和数据比较工具 最近线上数据需要进行架构比较,比较两个服务器上的mysql实例上数据库的架构 数据比较可以用percona的pt-table-checksum和pt- ...

  10. BZOJ4636&colon; 蒟蒻的数列&lpar;动态开节点线段树&rpar;

    题意 题目链接 Sol 直接上动态开节点线段树 因为只有一次询问,所以中途不需要下传标记 #include<bits/stdc++.h> #define LL long long usin ...