一 isinstance(obj,cls)和issubclass(sub,super)

1.1,isinstance(obj,cls)检查是否obj是否是类 cls 的对象

  1. class Foo(object):
  2. pass
  3. obj = Foo()
  4. isinstance(obj, Foo) #True

  

1.2,issubclass(sub, super)检查sub类是否是 super 类的派生类

  1. class Foo(object):
  2. pass
  3. class Bar(Foo):
  4. pass
  5. issubclass(Bar, Foo)  #True

  

二 反射

0,前戏

  先举一个例子,我们看看为什么要用反射

  1. #反射,通过字符串映射到对象的属性
  2. #首先这个例子,我们可以看出访问类或者对象的属性的时候,我们是对象.属性 类.属性
  3. #所以实际上,点后面都是属性,而不是字符串,但是我们需要字符串
  4. class People:
  5. def __init__(self,name,age):
  6. self.name = name
  7. self.age = age
  8. def talk(self):
  9. print('%s is talking'%self.name)
  10.  
  11. obj = People('huard',18)
  12. print(obj.name)
  13. print(obj.talk)
  14. # huard
  15. # <bound method People.talk of <__main__.People object at 0x000001C3FE8DC048>>
  16. #当用户输入字符串的时候,如何映射到一个对象的属性?
  17. # 比如
  18. choice = input(">>>")
  19. print(obj.choice)

  

1, 什么是反射

  反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。

2 ,python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)

  四个可以实现自省的函数

下列方法适用于类和对象(一切皆对象,类本身也是一个对象)

2.1,hasattr(object,name) 判断object中有没有对应的方法和属性

  1. 判断object中有没有一个name字符串对应的方法或属性
  1. class People:
  2. def __init__(self,name,age):
  3. self.name = name
  4. self.age = age
  5. def talk(self):
  6. print('%s is talking'%self.name)
  7. obj = People('huard',18)
  8. print(hasattr(obj,'name'))
  9. print(hasattr(obj,'talk'))
  10. print(hasattr(obj,'age'))
  11. # 结果:
  12. # True
  13. # True
  14. # True

  

2.2,getattr(object, name, default=None)  获取object中有没有对应的方法和属性

  1. def getattr(object, name, default=None): # known special case of getattr
  2. """
  3. getattr(object, name[, default]) -> value
  4.  
  5. Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y.
  6. When a default argument is given, it is returned when the attribute doesn't
  7. exist; without it, an exception is raised in that case.
  8. """
  9. pass

  

  1. class People:
  2. def __init__(self,name,age):
  3. self.name = name
  4. self.age = age
  5. def talk(self):
  6. print('%s is talking'%self.name)
  7. obj = People('huard',18)
  8. print(getattr(obj,'name'))
  9. print(getattr(obj,'talk'))
  10. print(getattr(obj,'age',default=None))
  11. print(getattr(obj,'ads',None))
  12. # 结果:(如果有的话 就返回值,没有的话就返回None)
  13. # huard
  14. # <bound method People.talk of <__main__.People object at 0x000001661CE1CBA8>>
  15. # 18
  16. # None

 

2.3,setattr(x, y, v) 设置对象及其属性

  1. def setattr(x, y, v): # real signature unknown; restored from __doc__
  2. """
  3. Sets the named attribute on the given object to the specified value.
  4.  
  5. setattr(x, 'y', v) is equivalent to ``x.y = v''
  6. """
  7. pass
  1. class People:
  2. def __init__(self,name,age):
  3. self.name = name
  4. self.age = age
  5. def talk(self):
  6. print('%s is talking'%self.name)
  7. obj = People('huard',18)
  8. setattr(obj,'sex','male')
  9. print(obj.__dict__)
  10. print(obj.sex)
  11. # 结果:
  12. # {'name': 'huard', 'age': 18, 'sex': 'male'}
  13. # male

  

2.4,delattr(x, y) 删除类或对象的属性

  1. def delattr(x, y): # real signature unknown; restored from __doc__
  2. """
  3. Deletes the named attribute from the given object.
  4.  
  5. delattr(x, 'y') is equivalent to ``del x.y''
  6. """
  7. pass
  1. class People:
  2. def __init__(self,name,age):
  3. self.name = name
  4. self.age = age
  5. def talk(self):
  6. print('%s is talking'%self.name)
  7.  
  8. obj = People('huard', 18)
  9. delattr(obj, 'age')
  10. print(obj.__dict__)
  11. # 结果:
  12. # {'name': 'huard'}

  

 

2.5,四个方法的使用演示

  1. class BlackMedium:
  2. feature='Ugly'
  3. def __init__(self,name,addr):
  4. self.name=name
  5. self.addr=addr
  6.  
  7. def sell_house(self):
  8. print('%s 黑中介卖房子啦,,但是谁能证明自己不mai' %self.name)
  9. def rent_house(self):
  10. print('%s 黑中介租房子啦,才租呢' %self.name)
  11.  
  12. b1=BlackMedium('万成置地','回龙观天露园')
  13.  
  14. #检测是否含有某属性
  15. print(hasattr(b1,'name')) #True
  16. print(hasattr(b1,'sell_house')) #True
  17.  
  18. #获取属性
  19. n=getattr(b1,'name')
  20. print(n) #万成置地
  21. func=getattr(b1,'rent_house')
  22. func() #万成置地 黑中介租房子啦,才租呢
  23.  
  24. # getattr(b1,'aaaaaaaa') #报错
  25. ''' getattr(b1,'aaaaaaaa') #报错
  26. AttributeError: 'BlackMedium' object has no attribute 'aaaaaaaa'
  27. '''
  28. #为了不让报错,我们提前设置异常处理,如果没有的话 直接读取的是我们设置的
  29. print(getattr(b1,'aaaaaaaa','不存在啊')) #不存在啊
  30.  
  31. #设置属性
  32. setattr(b1,'sb',True)
  33. setattr(b1,'show_name',lambda self:self.name+'sb')
  34. print(b1.__dict__)
  35. # {'name': '万成置地', 'addr': '回龙观天露园', 'sb': True, 'show_name': <function <lambda> at 0x000001A26A0E56A8>}
  36. print(b1.show_name(b1))
  37. # 万成置地sb
  38.  
  39. #删除属性
  40. delattr(b1,'addr')
  41. delattr(b1,'show_name')
  42. # delattr(b1,'show_name111')#不存在,则报错AttributeError: show_name111
  43.  
  44. print(b1.__dict__) #{'name': '万成置地', 'sb': True}

  

2.6,类也是对象

  1. class Foo(object):
  2.  
  3. staticField = "old boy"
  4.  
  5. def __init__(self):
  6. self.name = 'wupeiqi'
  7.  
  8. def func(self):
  9. return 'func'
  10.  
  11. @staticmethod
  12. def bar():
  13. return 'bar'
  14.  
  15. print(getattr(Foo, 'staticField'))
  16. print(getattr(Foo, 'func'))
  17. print(getattr(Foo, 'bar'))
  18. # old boy
  19. # <function Foo.func at 0x00000240E3205A60>
  20. # <function Foo.bar at 0x00000240E3205AE8>

  

2.7,反射当前模块成员

  1. import sys
  2.  
  3. def s1():
  4. print('s1')
  5.  
  6. def s2():
  7. print('s2')
  8.  
  9. this_module = sys.modules[__name__]
  10.  
  11. print(hasattr(this_module, 's1'))
  12. print(getattr(this_module, 's2'))
  13. # True
  14. # <function s2 at 0x0000020F3F0A59D8>

  

2.8,导入其他模块,利用反射查找该模块是否存在某个方法

  1. import module_test as obj
  2.  
  3. #obj.test()
  4.  
  5. print(hasattr(obj,'test'))
  6.  
  7. getattr(obj,'test')()
  8. # True
  9. # from the test

  

module_test.py

  1. # _*_ coding: utf-8 _*_
  2. def test():
  3. print('from the test')

  

3 为什么用反射?(反射的好处)

好处一:实现可插拔机制

  有俩程序员,一个james,一个是dunart,james在写程序的时候需要用到dunart所写的类,但是dunart去跟女朋友度蜜月去了,还没有完成他写的类,james想到了反射,使用了反射机制james可以继续完成自己的代码,等dunart度蜜月回来后再继续完成类的定义并且去实现james想要的功能。

  总之反射的好处就是,可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,什么意思?即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能

dunart还没有实现全部功能

  1. class FtpClient:
  2. 'ftp客户端,但是还么有实现具体的功能'
  3. def __init__(self,addr):
  4. print('正在连接服务器[%s]' %addr)
  5. self.addr=addr

  

不影响james的代码编写

  1. #from module import FtpClient
  2. f1=FtpClient('192.168.1.1')
  3. if hasattr(f1,'get'):
  4. func_get=getattr(f1,'get')
  5. func_get()
  6. else:
  7. print('---->不存在此方法')
  8. print('处理其他的逻辑')

  

好处二:动态导入模块(基于反射当前模块成员)

三 __setattr__,__delattr__,__getattr__

3.1,三者的用法演示

  1. class Foo:
  2. x=1
  3. def __init__(self,y):
  4. self.y=y
  5.  
  6. def __getattr__(self, item):
  7. print('----> from getattr:你找的属性不存在')
  8.  
  9. def __setattr__(self, key, value):
  10. print('----> from setattr')
  11. # self.key=value #这就无限递归了,你好好想想
  12. # self.__dict__[key]=value #应该使用它
  13.  
  14. def __delattr__(self, item):
  15. print('----> from delattr')
  16. # del self.item #无限递归了
  17. self.__dict__.pop(item)
  18.  
  19. #__setattr__添加/修改属性会触发它的执行
  20. f1=Foo(10)
  21. print(f1.__dict__) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值
  22. f1.z=3
  23. print(f1.__dict__)
  24.  
  25. #__delattr__删除属性的时候会触发
  26. f1.__dict__['a']=3#我们可以直接修改属性字典,来完成添加/修改属性的操作
  27. del f1.a
  28. print(f1.__dict__)
  29.  
  30. #__getattr__只有在使用点调用属性且属性不存在的时候才会触发
  31. f1.xxxxxx

  

四 二次加工标准类型(包装)

  包装:python为大家提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法,这就用到了我们刚学的继承/派生知识(其他的标准类型均可以通过下面的方式进行二次加工)

4.1,二次加工标准类型(基于继承实现)

  1. class List(list): #继承list所有的属性,也可以派生出自己新的,比如append和mid
  2. def append(self, p_object):
  3. ' 派生自己的append:加上类型检查'
  4. if not isinstance(p_object,int):
  5. raise TypeError('must be int')
  6. super().append(p_object)
  7.  
  8. @property
  9. def mid(self):
  10. '新增自己的属性'
  11. index=len(self)//2
  12. return self[index]
  13.  
  14. l=List([1,2,3,4])
  15. print(l)
  16. l.append(5)
  17. print(l)
  18. # l.append('1111111') #报错,必须为int类型
  19.  
  20. print(l.mid)
  21.  
  22. #其余的方法都继承list的
  23. l.insert(0,-123)
  24. print(l)
  25. l.clear()
  26. print(l)

  

4.2,练习(clear加权限限制)

  1. class List(list):
  2. def __init__(self,item,tag=False):
  3. super().__init__(item)
  4. self.tag=tag
  5. def append(self, p_object):
  6. if not isinstance(p_object,str):
  7. raise TypeError
  8. super().append(p_object)
  9. def clear(self):
  10. if not self.tag:
  11. raise PermissionError
  12. super().clear()
  13.  
  14. l=List([1,2,3],False)
  15. print(l)
  16. print(l.tag)
  17.  
  18. l.append('saf')
  19. print(l)
  20.  
  21. # l.clear() #异常
  22.  
  23. l.tag=True
  24. l.clear()

  

  授权:授权是包装的一个特性, 包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能。其它的则保持原样。授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。

4.3,实现授权的关键点就是覆盖__getattr__方法

授权示范一:

  1. import time
  2. class FileHandle:
  3. def __init__(self,filename,mode='r',encoding='utf-8'):
  4. self.file=open(filename,mode,encoding=encoding)
  5. def write(self,line):
  6. t=time.strftime('%Y-%m-%d %T')
  7. self.file.write('%s %s' %(t,line))
  8.  
  9. def __getattr__(self, item):
  10. return getattr(self.file,item)
  11.  
  12. f1=FileHandle('b.txt','w+')
  13. f1.write('你好啊')
  14. f1.seek(0)
  15. print(f1.read())
  16. f1.close()

  

授权示范二:

  1. #_*_coding:utf-8_*_
  2.  
  3. #我们来加上b模式支持
  4. import time
  5. class FileHandle:
  6. def __init__(self,filename,mode='r',encoding='utf-8'):
  7. if 'b' in mode:
  8. self.file=open(filename,mode)
  9. else:
  10. self.file=open(filename,mode,encoding=encoding)
  11. self.filename=filename
  12. self.mode=mode
  13. self.encoding=encoding
  14.  
  15. def write(self,line):
  16. if 'b' in self.mode:
  17. if not isinstance(line,bytes):
  18. raise TypeError('must be bytes')
  19. self.file.write(line)
  20.  
  21. def __getattr__(self, item):
  22. return getattr(self.file,item)
  23.  
  24. def __str__(self):
  25. if 'b' in self.mode:
  26. res="<_io.BufferedReader name='%s'>" %self.filename
  27. else:
  28. res="<_io.TextIOWrapper name='%s' mode='%s' encoding='%s'>" %(self.filename,self.mode,self.encoding)
  29. return res
  30. f1=FileHandle('b.txt','wb')
  31. # f1.write('你好啊啊啊啊啊') #自定制的write,不用在进行encode转成二进制去写了,简单,大气
  32. f1.write('你好啊'.encode('utf-8'))
  33. print(f1)
  34. f1.close()

  

练习题(授权)

  1. #练习一
  2. class List:
  3. def __init__(self,seq):
  4. self.seq=seq
  5.  
  6. def append(self, p_object):
  7. ' 派生自己的append加上类型检查,覆盖原有的append'
  8. if not isinstance(p_object,int):
  9. raise TypeError('must be int')
  10. self.seq.append(p_object)
  11.  
  12. @property
  13. def mid(self):
  14. '新增自己的方法'
  15. index=len(self.seq)//2
  16. return self.seq[index]
  17.  
  18. def __getattr__(self, item):
  19. return getattr(self.seq,item)
  20.  
  21. def __str__(self):
  22. return str(self.seq)
  23.  
  24. l=List([1,2,3])
  25. print(l)
  26. l.append(4)
  27. print(l)
  28. # l.append('3333333') #报错,必须为int类型
  29.  
  30. print(l.mid)
  31.  
  32. #基于授权,获得insert方法
  33. l.insert(0,-123)
  34. print(l)
  35.  
  36. #练习二
  37. class List:
  38. def __init__(self,seq,permission=False):
  39. self.seq=seq
  40. self.permission=permission
  41. def clear(self):
  42. if not self.permission:
  43. raise PermissionError('not allow the operation')
  44. self.seq.clear()
  45.  
  46. def __getattr__(self, item):
  47. return getattr(self.seq,item)
  48.  
  49. def __str__(self):
  50. return str(self.seq)
  51. l=List([1,2,3])
  52. # l.clear() #此时没有权限,抛出异常
  53.  
  54. l.permission=True
  55. print(l)
  56. l.clear()
  57. print(l)
  58.  
  59. #基于授权,获得insert方法
  60. l.insert(0,-123)
  61. print(l)
  62.  
  63. 练习题(授权)

  

五 __getattribute__

5.1,回顾__getattr__

  1. class Foo:
  2. def __init__(self,x):
  3. self.x=x
  4.  
  5. def __getattr__(self, item):
  6. print('执行的是我')
  7. # return self.__dict__[item]
  8.  
  9. f1=Foo(10)
  10. print(f1.x)
  11. f1.xxxxxx #不存在的属性访问,触发__getattr__

  

5.2,getattribute

  1. class Foo:
  2. def __init__(self,x):
  3. self.x=x
  4.  
  5. def __getattribute__(self, item):
  6. print('不管是否存在,我都会执行')
  7.  
  8. f1=Foo(10)
  9. f1.x
  10. f1.xxxxxx

  

5.3,二者同时出现

  1. #_*_coding:utf-8_*_
  2.  
  3. class Foo:
  4. def __init__(self,x):
  5. self.x=x
  6.  
  7. def __getattr__(self, item):
  8. print('执行的是我')
  9. # return self.__dict__[item]
  10. def __getattribute__(self, item):
  11. print('不管是否存在,我都会执行')
  12. raise AttributeError('哈哈')
  13.  
  14. f1=Foo(10)
  15. f1.x
  16. f1.xxxxxx
  17.  
  18. #当__getattribute__与__getattr__同时存在,只会执行__getattrbute__,
  19. 除非__getattribute__在执行过程中抛出异常AttributeError

  

六 描述符(__get__,__set__,__delete__)

  1 描述符是什么:描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议  

  • __get__():调用一个属性时,触发
  • __set__():为一个属性赋值时,触发
  • __delete__():采用del删除属性时,触发

定义一个描述符

  1. class Foo: #在python3中Foo是新式类,它实现了三种方法,这个类就被称作一个描述符
  2. def __get__(self, instance, owner):
  3. pass
  4. def __set__(self, instance, value):
  5. pass
  6. def __delete__(self, instance):
  7. pass

  

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

-0,引子:描述符类产生的实例进行属性操作并不会触发三个方法的执行

  1. class Foo:
  2. def __get__(self, instance, owner):
  3. print('触发get')
  4. def __set__(self, instance, value):
  5. print('触发set')
  6. def __delete__(self, instance):
  7. print('触发delete')
  8.  
  9. #包含这三个方法的新式类称为描述符,由这个类产生的实例进行属性的调用/赋值/删除,并不会触发这三个方法
  10. f1=Foo()
  11. f1.name='egon'
  12. f1.name
  13. del f1.name
  14. #疑问:何时,何地,会触发这三个方法的执行

  

-1,描述符应用之何时?何地?

  1. #描述符Str
  2. class Str:
  3. def __get__(self, instance, owner):
  4. print('Str调用')
  5. def __set__(self, instance, value):
  6. print('Str设置...')
  7. def __delete__(self, instance):
  8. print('Str删除...')
  9.  
  10. #描述符Int
  11. class Int:
  12. def __get__(self, instance, owner):
  13. print('Int调用')
  14. def __set__(self, instance, value):
  15. print('Int设置...')
  16. def __delete__(self, instance):
  17. print('Int删除...')
  18.  
  19. class People:
  20. name=Str()
  21. age=Int()
  22. def __init__(self,name,age): #name被Str类代理,age被Int类代理,
  23. self.name=name
  24. self.age=age
  25.  
  26. #何地?:定义成另外一个类的类属性
  27.  
  28. #何时?:且看下列演示
  29.  
  30. p1=People('alex',18)
  31.  
  32. #描述符Str的使用
  33. p1.name
  34. p1.name='egon'
  35. del p1.name
  36.  
  37. #描述符Int的使用
  38. p1.age
  39. p1.age=18
  40. del p1.age
  41.  
  42. #我们来瞅瞅到底发生了什么
  43. print(p1.__dict__)
  44. print(People.__dict__)
  45.  
  46. #补充
  47. print(type(p1) == People) #type(obj)其实是查看obj是由哪个类实例化来的
  48. print(type(p1).__dict__ == People.__dict__)

  

-2,描述符分两种 一 数据描述符:至少实现了__get__()和__set__()

  1. class Foo:
  2. def __set__(self, instance, value):
  3. print('set')
  4. def __get__(self, instance, owner):
  5. print('get')

  

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

  1. class Foo:
  2. def __get__(self, instance, owner):
  3. print('get')

  

4 注意事项:

  • 一 描述符本身应该定义成新式类,被代理的类也应该是新式类
  • 二 必须把描述符定义成这个类的类属性,不能为定义到构造函数中
  • 三 要严格遵循该优先级,优先级由高到底分别是
    1. 1.类属性
    2. 2.数据描述符
    3. 3.实例属性
    4. 4.非数据描述符
    5. 5.找不到的属性触发__getattr__()

      

类属性>数据描述符

  1. #描述符Str
  2. class Str:
  3. def __get__(self, instance, owner):
  4. print('Str调用')
  5. def __set__(self, instance, value):
  6. print('Str设置...')
  7. def __delete__(self, instance):
  8. print('Str删除...')
  9.  
  10. class People:
  11. name=Str()
  12. def __init__(self,name,age): #name被Str类代理,age被Int类代理,
  13. self.name=name
  14. self.age=age
  15.  
  16. #基于上面的演示,我们已经知道,在一个类中定义描述符它就是一个类属性,
  17. 存在于类的属性字典中,而不是实例的属性字典
  18.  
  19. #那既然描述符被定义成了一个类属性,直接通过类名也一定可以调用吧,没错
  20. People.name #恩,调用类属性name,本质就是在调用描述符Str,触发了__get__()
  21.  
  22. People.name='egon' #那赋值呢,我去,并没有触发__set__()
  23. del People.name #赶紧试试del,我去,也没有触发__delete__()
  24. #结论:描述符对类没有作用-------->傻逼到家的结论
  25.  
  26. '''
  27. 原因:描述符在使用时被定义成另外一个类的类属性,因而类属性比二次加工的描述符
  28. 伪装而来的类属性有更高的优先级
  29. People.name #恩,调用类属性name,找不到就去找描述符伪装的类属性name,触发了__get__()
  30.  
  31. People.name='egon' #那赋值呢,直接赋值了一个类属性,它拥有更高的优先级,
  32. 相当于覆盖了描述符,肯定不会触发描述符的__set__()
  33. del People.name #同上
  34. '''

  

数据描述符>实例属性

  1. #描述符Str
  2. class Str:
  3. def __get__(self, instance, owner):
  4. print('Str调用')
  5. def __set__(self, instance, value):
  6. print('Str设置...')
  7. def __delete__(self, instance):
  8. print('Str删除...')
  9.  
  10. class People:
  11. name=Str()
  12. def __init__(self,name,age): #name被Str类代理,age被Int类代理,
  13. self.name=name
  14. self.age=age
  15.  
  16. p1=People('egon',18)
  17.  
  18. #如果描述符是一个数据描述符(即有__get__又有__set__),那么p1.name的调
  19. 用与赋值都是触发描述符的操作,于p1本身无关了,相当于覆盖了实例的属性
  20. p1.name='egonnnnnn'
  21. p1.name
  22. print(p1.__dict__)#实例的属性字典中没有name,因为name是一个数据描述符,
  23. 优先级高于实例属性,查看/赋值/删除都是跟描述符有关,与实例无关了
  24. del p1.name

  

实例属性>非数据描述符

  1. class Foo:
  2. def func(self):
  3. print('我胡汉三又回来了')
  4. f1=Foo()
  5. f1.func() #调用类的方法,也可以说是调用非数据描述符
  6. #函数是一个非数据描述符对象(一切皆对象么)
  7. print(dir(Foo.func))
  8. print(hasattr(Foo.func,'__set__'))
  9. print(hasattr(Foo.func,'__get__'))
  10. print(hasattr(Foo.func,'__delete__'))
  11. #有人可能会问,描述符不都是类么,函数怎么算也应该是一个对象啊,怎么就是描述符了
  12. #笨蛋哥,描述符是类没问题,描述符在应用的时候不都是实例化成一个类属性么
  13. #函数就是一个由非描述符类实例化得到的对象
  14. #没错,字符串也一样
  15.  
  16. f1.func='这是实例属性啊'
  17. print(f1.func)
  18.  
  19. del f1.func #删掉了非数据
  20. f1.func()

  

再次验证:实例属性>非数据描述符

  1. class Foo:
  2. def __set__(self, instance, value):
  3. print('set')
  4. def __get__(self, instance, owner):
  5. print('get')
  6. class Room:
  7. name=Foo()
  8. def __init__(self,name,width,length):
  9. self.name=name
  10. self.width=width
  11. self.length=length
  12.  
  13. #name是一个数据描述符,因为name=Foo()而Foo实现了get和set方法,因而比实例属性有更高的优先级
  14. #对实例的属性操作,触发的都是描述符的
  15. r1=Room('厕所',1,1)
  16. r1.name
  17. r1.name='厨房'
  18.  
  19. class Foo:
  20. def __get__(self, instance, owner):
  21. print('get')
  22. class Room:
  23. name=Foo()
  24. def __init__(self,name,width,length):
  25. self.name=name
  26. self.width=width
  27. self.length=length
  28.  
  29. #name是一个非数据描述符,因为name=Foo()而Foo没有实现set方法,因而比实例属性有更低的优先级
  30. #对实例的属性操作,触发的都是实例自己的
  31. r1=Room('厕所',1,1)
  32. r1.name
  33. r1.name='厨房'

  

非数据描述符>找不到

  1. class Foo:
  2. def func(self):
  3. print('我胡汉三又回来了')
  4.  
  5. def __getattr__(self, item):
  6. print('找不到了当然是来找我啦',item)
  7. f1=Foo()
  8.  
  9. f1.xxxxxxxxxxx

  

5 描述符使用

众所周知,python是弱类型语言,即参数的赋值没有类型限制,下面我们通过描述符机制来实现类型限制功能

牛刀小试

  1. class Str:
  2. def __init__(self,name):
  3. self.name=name
  4. def __get__(self, instance, owner):
  5. print('get--->',instance,owner)
  6. return instance.__dict__[self.name]
  7.  
  8. def __set__(self, instance, value):
  9. print('set--->',instance,value)
  10. instance.__dict__[self.name]=value
  11. def __delete__(self, instance):
  12. print('delete--->',instance)
  13. instance.__dict__.pop(self.name)
  14.  
  15. class People:
  16. name=Str('name')
  17. def __init__(self,name,age,salary):
  18. self.name=name
  19. self.age=age
  20. self.salary=salary
  21.  
  22. p1=People('egon',18,3231.3)
  23.  
  24. #调用
  25. print(p1.__dict__)
  26. p1.name
  27.  
  28. #赋值
  29. print(p1.__dict__)
  30. p1.name='egonlin'
  31. print(p1.__dict__)
  32.  
  33. #删除
  34. print(p1.__dict__)
  35. del p1.name
  36. print(p1.__dict__)

  

拔刀相助

  1. class Str:
  2. def __init__(self,name):
  3. self.name=name
  4. def __get__(self, instance, owner):
  5. print('get--->',instance,owner)
  6. return instance.__dict__[self.name]
  7.  
  8. def __set__(self, instance, value):
  9. print('set--->',instance,value)
  10. instance.__dict__[self.name]=value
  11. def __delete__(self, instance):
  12. print('delete--->',instance)
  13. instance.__dict__.pop(self.name)
  14.  
  15. class People:
  16. name=Str('name')
  17. def __init__(self,name,age,salary):
  18. self.name=name
  19. self.age=age
  20. self.salary=salary
  21.  
  22. #疑问:如果我用类名去操作属性呢
  23. People.name #报错,错误的根源在于类去操作属性时,会把None传给instance
  24.  
  25. #修订__get__方法
  26. class Str:
  27. def __init__(self,name):
  28. self.name=name
  29. def __get__(self, instance, owner):
  30. print('get--->',instance,owner)
  31. if instance is None:
  32. return self
  33. return instance.__dict__[self.name]
  34.  
  35. def __set__(self, instance, value):
  36. print('set--->',instance,value)
  37. instance.__dict__[self.name]=value
  38. def __delete__(self, instance):
  39. print('delete--->',instance)
  40. instance.__dict__.pop(self.name)
  41.  
  42. class People:
  43. name=Str('name')
  44. def __init__(self,name,age,salary):
  45. self.name=name
  46. self.age=age
  47. self.salary=salary
  48. print(People.name) #完美,解决

  

磨刀霍霍

  1. class Str:
  2. def __init__(self,name,expected_type):
  3. self.name=name
  4. self.expected_type=expected_type
  5. def __get__(self, instance, owner):
  6. print('get--->',instance,owner)
  7. if instance is None:
  8. return self
  9. return instance.__dict__[self.name]
  10.  
  11. def __set__(self, instance, value):
  12. print('set--->',instance,value)
  13. if not isinstance(value,self.expected_type): #如果不是期望的类型,则抛出异常
  14. raise TypeError('Expected %s' %str(self.expected_type))
  15. instance.__dict__[self.name]=value
  16. def __delete__(self, instance):
  17. print('delete--->',instance)
  18. instance.__dict__.pop(self.name)
  19.  
  20. class People:
  21. name=Str('name',str) #新增类型限制str
  22. def __init__(self,name,age,salary):
  23. self.name=name
  24. self.age=age
  25. self.salary=salary
  26.  
  27. p1=People(123,18,3333.3)#传入的name因不是字符串类型而抛出异常

  

大刀阔斧

  1. class Typed:
  2. def __init__(self,name,expected_type):
  3. self.name=name
  4. self.expected_type=expected_type
  5. def __get__(self, instance, owner):
  6. print('get--->',instance,owner)
  7. if instance is None:
  8. return self
  9. return instance.__dict__[self.name]
  10.  
  11. def __set__(self, instance, value):
  12. print('set--->',instance,value)
  13. if not isinstance(value,self.expected_type):
  14. raise TypeError('Expected %s' %str(self.expected_type))
  15. instance.__dict__[self.name]=value
  16. def __delete__(self, instance):
  17. print('delete--->',instance)
  18. instance.__dict__.pop(self.name)
  19.  
  20. class People:
  21. name=Typed('name',str)
  22. age=Typed('name',int)
  23. salary=Typed('name',float)
  24. def __init__(self,name,age,salary):
  25. self.name=name
  26. self.age=age
  27. self.salary=salary
  28.  
  29. p1=People(123,18,3333.3)
  30. p1=People('egon','18',3333.3)
  31. p1=People('egon',18,3333)

  

大刀阔斧之后我们已然能实现功能了,但是问题是,如果我们的类有很多属性,你仍然采用在定义一堆类属性的方式去实现,low,这时候我需要教你一招:独孤九剑

类的装饰器:无参

  1. def decorate(cls):
  2. print('类的装饰器开始运行啦------>')
  3. return cls
  4.  
  5. @decorate #无参:People=decorate(People)
  6. class People:
  7. def __init__(self,name,age,salary):
  8. self.name=name
  9. self.age=age
  10. self.salary=salary
  11.  
  12. p1=People('egon',18,3333.3)

  

类的装饰器:有参

  1. def typeassert(**kwargs):
  2. def decorate(cls):
  3. print('类的装饰器开始运行啦------>',kwargs)
  4. return cls
  5. return decorate
  6. @typeassert(name=str,age=int,salary=float) #有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People)
  7. class People:
  8. def __init__(self,name,age,salary):
  9. self.name=name
  10. self.age=age
  11. self.salary=salary
  12.  
  13. p1=People('egon',18,3333.3)

  

终极大招

刀光剑影

  1. class Typed:
  2. def __init__(self,name,expected_type):
  3. self.name=name
  4. self.expected_type=expected_type
  5. def __get__(self, instance, owner):
  6. print('get--->',instance,owner)
  7. if instance is None:
  8. return self
  9. return instance.__dict__[self.name]
  10.  
  11. def __set__(self, instance, value):
  12. print('set--->',instance,value)
  13. if not isinstance(value,self.expected_type):
  14. raise TypeError('Expected %s' %str(self.expected_type))
  15. instance.__dict__[self.name]=value
  16. def __delete__(self, instance):
  17. print('delete--->',instance)
  18. instance.__dict__.pop(self.name)
  19.  
  20. def typeassert(**kwargs):
  21. def decorate(cls):
  22. print('类的装饰器开始运行啦------>',kwargs)
  23. for name,expected_type in kwargs.items():
  24. setattr(cls,name,Typed(name,expected_type))
  25. return cls
  26. return decorate
  27. @typeassert(name=str,age=int,salary=float) #有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People)
  28. class People:
  29. def __init__(self,name,age,salary):
  30. self.name=name
  31. self.age=age
  32. self.salary=salary
  33.  
  34. print(People.__dict__)
  35. p1=People('egon',18,3333.3)

  

6 描述符总结

描述符是可以实现大部分python类特性中的底层魔法,包括@classmethod,@staticmethd,@property甚至是__slots__属性

描述父是很多高级库和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件.

7 利用描述符原理完成一个自定制@property,实现延迟计算(本质就是把一个函数属性利用装饰器原理做成一个描述符:类的属性字典中函数名为key,value为描述符类产生的对象)

@property回顾

  1. class Room:
  2. def __init__(self,name,width,length):
  3. self.name=name
  4. self.width=width
  5. self.length=length
  6.  
  7. @property
  8. def area(self):
  9. return self.width * self.length
  10.  
  11. r1=Room('alex',1,1)
  12. print(r1.area)

  

自己做一个@property

  1. class Lazyproperty:
  2. def __init__(self,func):
  3. self.func=func
  4. def __get__(self, instance, owner):
  5. print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()')
  6. if instance is None:
  7. return self
  8. return self.func(instance) #此时你应该明白,到底是谁在为你做自动传递self的事情
  9.  
  10. class Room:
  11. def __init__(self,name,width,length):
  12. self.name=name
  13. self.width=width
  14. self.length=length
  15.  
  16. @Lazyproperty #area=Lazyproperty(area) 相当于定义了一个类属性,即描述符
  17. def area(self):
  18. return self.width * self.length
  19.  
  20. r1=Room('alex',1,1)
  21. print(r1.area)

  

实现延迟计算功能

  1. class Lazyproperty:
  2. def __init__(self,func):
  3. self.func=func
  4. def __get__(self, instance, owner):
  5. print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()')
  6. if instance is None:
  7. return self
  8. else:
  9. print('--->')
  10. value=self.func(instance)
  11. setattr(instance,self.func.__name__,value) #计算一次就缓存到实例的属性字典中
  12. return value
  13.  
  14. class Room:
  15. def __init__(self,name,width,length):
  16. self.name=name
  17. self.width=width
  18. self.length=length
  19.  
  20. @Lazyproperty #area=Lazyproperty(area) 相当于'定义了一个类属性,即描述符'
  21. def area(self):
  22. return self.width * self.length
  23.  
  24. r1=Room('alex',1,1)
  25. print(r1.area) #先从自己的属性字典找,没有再去类的中找,然后出发了area的__get__方法
  26. print(r1.area) #先从自己的属性字典找,找到了,是上次计算的结果,这样就不用每执行一次都去计算

  

一个小的改动,延迟计算的美梦就破碎了

  1. #缓存不起来了
  2.  
  3. class Lazyproperty:
  4. def __init__(self,func):
  5. self.func=func
  6. def __get__(self, instance, owner):
  7. print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()')
  8. if instance is None:
  9. return self
  10. else:
  11. value=self.func(instance)
  12. instance.__dict__[self.func.__name__]=value
  13. return value
  14. # return self.func(instance) #此时你应该明白,到底是谁在为你做自动传递self的事情
  15. def __set__(self, instance, value):
  16. print('hahahahahah')
  17.  
  18. class Room:
  19. def __init__(self,name,width,length):
  20. self.name=name
  21. self.width=width
  22. self.length=length
  23.  
  24. @Lazyproperty #area=Lazyproperty(area) 相当于定义了一个类属性,即描述符
  25. def area(self):
  26. return self.width * self.length
  27.  
  28. print(Room.__dict__)
  29. r1=Room('alex',1,1)
  30. print(r1.area)
  31. print(r1.area)
  32. print(r1.area)
  33. print(r1.area) #缓存功能失效,每次都去找描述符了,为何,因为描述符实现了set方法,它由非数据描述符变成了数据描述符,
  34. 数据描述符比实例属性有更高的优先级,因而所有的属性操作都去找描述符了

  

8 利用描述符原理完成一个自定制@classmethod

自己做一个@classmethod

  1. class ClassMethod:
  2. def __init__(self,func):
  3. self.func=func
  4.  
  5. def __get__(self, instance, owner): #类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身,
  6. def feedback():
  7. print('在这里可以加功能啊...')
  8. return self.func(owner)
  9. return feedback
  10.  
  11. class People:
  12. name='linhaifeng'
  13. @ClassMethod # say_hi=ClassMethod(say_hi)
  14. def say_hi(cls):
  15. print('你好啊,帅哥 %s' %cls.name)
  16.  
  17. People.say_hi()
  18.  
  19. p1=People()
  20. p1.say_hi()
  21. #疑问,类方法如果有参数呢,好说,好说
  22.  
  23. class ClassMethod:
  24. def __init__(self,func):
  25. self.func=func
  26.  
  27. def __get__(self, instance, owner): #类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身,
  28. def feedback(*args,**kwargs):
  29. print('在这里可以加功能啊...')
  30. return self.func(owner,*args,**kwargs)
  31. return feedback
  32.  
  33. class People:
  34. name='linhaifeng'
  35. @ClassMethod # say_hi=ClassMethod(say_hi)
  36. def say_hi(cls,msg):
  37. print('你好啊,帅哥 %s %s' %(cls.name,msg))
  38.  
  39. People.say_hi('你是那偷心的贼')
  40.  
  41. p1=People()
  42. p1.say_hi('你是那偷心的贼')

  

9 利用描述符原理完成一个自定制的@staticmethod

自己做一个@staticmethod

  1. class StaticMethod:
  2. def __init__(self,func):
  3. self.func=func
  4.  
  5. def __get__(self, instance, owner): #类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身,
  6. def feedback(*args,**kwargs):
  7. print('在这里可以加功能啊...')
  8. return self.func(*args,**kwargs)
  9. return feedback
  10.  
  11. class People:
  12. @StaticMethod# say_hi=StaticMethod(say_hi)
  13. def say_hi(x,y,z):
  14. print('------>',x,y,z)
  15.  
  16. People.say_hi(1,2,3)
  17.  
  18. p1=People()
  19. p1.say_hi(4,5,6)

  

六 再看property

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

用法一

  1. class Foo:
  2. @property
  3. def AAA(self):
  4. print('get的时候运行我啊')
  5.  
  6. @AAA.setter
  7. def AAA(self,value):
  8. print('set的时候运行我啊')
  9.  
  10. @AAA.deleter
  11. def AAA(self):
  12. print('delete的时候运行我啊')
  13.  
  14. #只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter
  15. f1=Foo()
  16. f1.AAA
  17. f1.AAA='aaa'
  18. del f1.AAA

  

用法二

  1. class Foo:
  2. def get_AAA(self):
  3. print('get的时候运行我啊')
  4.  
  5. def set_AAA(self,value):
  6. print('set的时候运行我啊')
  7.  
  8. def delete_AAA(self):
  9. print('delete的时候运行我啊')
  10. AAA=property(get_AAA,set_AAA,delete_AAA) #内置property三个参数与get,set,delete一一对应
  11.  
  12. f1=Foo()
  13. f1.AAA
  14. f1.AAA='aaa'
  15. del f1.AAA

  

怎么用?

案例一

  1. class Goods:
  2.  
  3. def __init__(self):
  4. # 原价
  5. self.original_price = 100
  6. # 折扣
  7. self.discount = 0.8
  8.  
  9. @property
  10. def price(self):
  11. # 实际价格 = 原价 * 折扣
  12. new_price = self.original_price * self.discount
  13. return new_price
  14.  
  15. @price.setter
  16. def price(self, value):
  17. self.original_price = value
  18.  
  19. @price.deleter
  20. def price(self):
  21. del self.original_price
  22.  
  23. obj = Goods()
  24. obj.price # 获取商品价格
  25. obj.price = 200 # 修改商品原价
  26. print(obj.price)
  27. del obj.price # 删除商品原价

  

案例二

  1. #实现类型检测功能
  2.  
  3. #第一关:
  4. class People:
  5. def __init__(self,name):
  6. self.name=name
  7.  
  8. @property
  9. def name(self):
  10. return self.name
  11.  
  12. # p1=People('alex') #property自动实现了set和get方法属于数据描述符,比实例属性优先级高,所以你这面写会触发property内置的set,抛出异常
  13.  
  14. #第二关:修订版
  15.  
  16. class People:
  17. def __init__(self,name):
  18. self.name=name #实例化就触发property
  19.  
  20. @property
  21. def name(self):
  22. # return self.name #无限递归
  23. print('get------>')
  24. return self.DouNiWan
  25.  
  26. @name.setter
  27. def name(self,value):
  28. print('set------>')
  29. self.DouNiWan=value
  30.  
  31. @name.deleter
  32. def name(self):
  33. print('delete------>')
  34. del self.DouNiWan
  35.  
  36. p1=People('alex') #self.name实际是存放到self.DouNiWan里
  37. print(p1.name)
  38. print(p1.name)
  39. print(p1.name)
  40. print(p1.__dict__)
  41.  
  42. p1.name='egon'
  43. print(p1.__dict__)
  44.  
  45. del p1.name
  46. print(p1.__dict__)
  47.  
  48. #第三关:加上类型检查
  49. class People:
  50. def __init__(self,name):
  51. self.name=name #实例化就触发property
  52.  
  53. @property
  54. def name(self):
  55. # return self.name #无限递归
  56. print('get------>')
  57. return self.DouNiWan
  58.  
  59. @name.setter
  60. def name(self,value):
  61. print('set------>')
  62. if not isinstance(value,str):
  63. raise TypeError('必须是字符串类型')
  64. self.DouNiWan=value
  65.  
  66. @name.deleter
  67. def name(self):
  68. print('delete------>')
  69. del self.DouNiWan
  70.  
  71. p1=People('alex') #self.name实际是存放到self.DouNiWan里
  72. p1.name=1

  

七 __setitem__,__getitem__,__delitem__

  1. class Foo:
  2. def __init__(self,name):
  3. self.name=name
  4.  
  5. def __getitem__(self, item):
  6. print(self.__dict__[item])
  7.  
  8. def __setitem__(self, key, value):
  9. self.__dict__[key]=value
  10. def __delitem__(self, key):
  11. print('del obj[key]时,我执行')
  12. self.__dict__.pop(key)
  13. def __delattr__(self, item):
  14. print('del obj.key时,我执行')
  15. self.__dict__.pop(item)
  16.  
  17. f1=Foo('sb')
  18. f1['age']=18
  19. f1['age1']=19
  20. del f1.age1
  21. del f1['age']
  22. f1['name']='alex'
  23. print(f1.__dict__)

  

八 __str__,__repr__,__format__

改变对象的字符串显示__str__,__repr__
自定制格式化字符串__format__

  1. #_*_coding:utf-8_*_
  2.  
  3. format_dict={
  4. 'nat':'{obj.name}-{obj.addr}-{obj.type}',#学校名-学校地址-学校类型
  5. 'tna':'{obj.type}:{obj.name}:{obj.addr}',#学校类型:学校名:学校地址
  6. 'tan':'{obj.type}/{obj.addr}/{obj.name}',#学校类型/学校地址/学校名
  7. }
  8. class School:
  9. def __init__(self,name,addr,type):
  10. self.name=name
  11. self.addr=addr
  12. self.type=type
  13.  
  14. def __repr__(self):
  15. return 'School(%s,%s)' %(self.name,self.addr)
  16. def __str__(self):
  17. return '(%s,%s)' %(self.name,self.addr)
  18.  
  19. def __format__(self, format_spec):
  20. # if format_spec
  21. if not format_spec or format_spec not in format_dict:
  22. format_spec='nat'
  23. fmt=format_dict[format_spec]
  24. return fmt.format(obj=self)
  25.  
  26. s1=School('oldboy1','北京','私立')
  27. print('from repr: ',repr(s1))
  28. print('from str: ',str(s1))
  29. print(s1)
  30.  
  31. '''
  32. str函数或者print函数--->obj.__str__()
  33. repr或者交互式解释器--->obj.__repr__()
  34. 如果__str__没有被定义,那么就会使用__repr__来代替输出
  35. 注意:这俩方法的返回值必须是字符串,否则抛出异常
  36. '''
  37. print(format(s1,'nat'))
  38. print(format(s1,'tna'))
  39. print(format(s1,'tan'))
  40. print(format(s1,'asfdasdffd'))

  

自定义format练习

  1. date_dic={
  2. 'ymd':'{0.year}:{0.month}:{0.day}',
  3. 'dmy':'{0.day}/{0.month}/{0.year}',
  4. 'mdy':'{0.month}-{0.day}-{0.year}',
  5. }
  6. class Date:
  7. def __init__(self,year,month,day):
  8. self.year=year
  9. self.month=month
  10. self.day=day
  11.  
  12. def __format__(self, format_spec):
  13. if not format_spec or format_spec not in date_dic:
  14. format_spec='ymd'
  15. fmt=date_dic[format_spec]
  16. return fmt.format(self)
  17.  
  18. d1=Date(2016,12,29)
  19. print(format(d1))
  20. print('{:mdy}'.format(d1))

  

issubclass和isinstance

  1. #_*_coding:utf-8_*_
  2.  
  3. class A:
  4. pass
  5.  
  6. class B(A):
  7. pass
  8.  
  9. print(issubclass(B,A)) #B是A的子类,返回True
  10.  
  11. a1=A()
  12. print(isinstance(a1,A)) #a1是A的实例

  

九 slots

slots使用

  1. 1.__slots__是什么:是一个类变量,变量值可以是列表,元祖,或者可迭代对象,
  2. 也可以是一个字符串(意味着所有实例只有一个数据属性)
  3.  
  4. 2.引子:使用点来访问属性本质就是在访问类或者对象的__dict__属性字典
  5. (类的字典是共享的,而每个实例的是独立的)
  6.  
  7. 3.为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,
  8. 但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__
  9. 当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。
  10. 实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个
  11. 字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到
  12. 这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给
  13. 实例添加新的属性了,只能使用在__slots__中定义的那些属性名。
  14.  
  15. 4.注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外,
  16. 定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,
  17. 你应该只在那些经常被使用到 的用作数据结构的类上定义__slots__比如在程序
  18. 中需要创建某个类的几百万个实例对象
  19. 关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增
  20. 加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。
  21. 更多的是用来作为一个内存优化工具。

  

刨根问底

  1. class Foo:
  2. __slots__=['name','age']
  3.  
  4. f1=Foo()
  5. f1.name='alex'
  6. f1.age=18
  7. print(f1.__slots__)
  8.  
  9. f2=Foo()
  10. f2.name='egon'
  11. f2.age=19
  12. print(f2.__slots__)
  13.  
  14. print(Foo.__dict__)
  15. #f1与f2都没有属性字典__dict__了,统一归__slots__管,节省内存

  

十 __next__和__iter__实现迭代器协议

简单示范

  1. #_*_coding:utf-8_*_
  2.  
  3. class Foo:
  4. def __init__(self,x):
  5. self.x=x
  6.  
  7. def __iter__(self):
  8. return self
  9.  
  10. def __next__(self):
  11. n=self.x
  12. self.x+=1
  13. return self.x
  14.  
  15. f=Foo(3)
  16. for i in f:
  17. print(i)
  18. class Foo:
  19. def __init__(self,start,stop):
  20. self.num=start
  21. self.stop=stop
  22. def __iter__(self):
  23. return self
  24. def __next__(self):
  25. if self.num >= self.stop:
  26. raise StopIteration
  27. n=self.num
  28. self.num+=1
  29. return n
  30.  
  31. f=Foo(1,5)
  32. from collections import Iterable,Iterator
  33. print(isinstance(f,Iterator))
  34.  
  35. for i in Foo(1,5):
  36. print(i)

  

练习:简单模拟range,加上步长

  1. class Range:
  2. def __init__(self,n,stop,step):
  3. self.n=n
  4. self.stop=stop
  5. self.step=step
  6.  
  7. def __next__(self):
  8. if self.n >= self.stop:
  9. raise StopIteration
  10. x=self.n
  11. self.n+=self.step
  12. return x
  13.  
  14. def __iter__(self):
  15. return self
  16.  
  17. for i in Range(1,7,3): #
  18. print(i)

  

斐波那契数列

  1. class Fib:
  2. def __init__(self):
  3. self._a=0
  4. self._b=1
  5.  
  6. def __iter__(self):
  7. return self
  8.  
  9. def __next__(self):
  10. self._a,self._b=self._b,self._a + self._b
  11. return self._a
  12.  
  13. f1=Fib()
  14.  
  15. print(f1.__next__())
  16. print(next(f1))
  17. print(next(f1))
  18.  
  19. for i in f1:
  20. if i > 100:
  21. break
  22. print('%s ' %i,end='')

  

十一 __doc__

它类的描述信息

  1. class Foo:
  2. '我是描述信息'
  3. pass
  4.  
  5. print(Foo.__doc__)

  

该属性无法被继承

  1. class Foo:
  2. '我是描述信息'
  3. pass
  4.  
  5. class Bar(Foo):
  6. pass
  7. print(Bar.__doc__) #该属性无法继承给子类

  

十二 __module__和__class__

__module__ 表示当前操作的对象在那个模块

__class__ 表示当前操作的对象的类是什么

lib/aa.py

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3.  
  4. class C:
  5.  
  6. def __init__(self):
  7. self.name = SB'

  

index.py

  1. from lib.aa import C
  2.  
  3. obj = C()
  4. print obj.__module__ # 输出 lib.aa,即:输出模块
  5. print obj.__class__ # 输出 lib.aa.C,即:输出类

  

十三 __del__

析构方法,当对象在内存中被释放时,自动触发执行。

注:如果产生的对象仅仅只是python程序级别的(用户级),那么无需定义__del__,如果产生的对象的同时还会向操作系统发起系统调用,即一个对象有用户级与内核级两种资源,比如(打开一个文件,创建一个数据库链接),则必须在清除对象的同时回收系统资源,这就用到了__del__。

简单示范

  1. class Foo:
  2.  
  3. def __del__(self):
  4. print('执行我啦')
  5.  
  6. f1=Foo()
  7. del f1
  8. print('------->')
  9.  
  10. #输出结果
  11. 执行我啦
  12. ------->

  

挖坑埋了你

  1. class Foo:
  2.  
  3. def __del__(self):
  4. print('执行我啦')
  5.  
  6. f1=Foo()
  7. # del f1
  8. print('------->')
  9.  
  10. #输出结果
  11. ------->
  12. 执行我啦
  13.  
  14. #为何啊???

  

典型的应用场景:

创建数据库类,用该类实例化出数据库链接对象,对象本身是存放于用户空间内存中,而链接则是由操作系统管理的,存放于内核空间内存中

当程序结束时,python只会回收自己的内存空间,即用户态内存,而操作系统的资源则没有被回收,这就需要我们定制__del__,在对象被删除前向操作系统发起关闭数据库链接的系统调用,回收资源

这与文件处理是一个道理:

  1. f=open('a.txt') #做了两件事,在用户空间拿到一个f变量,在操作系统内核空间打开一个文件
  2. del f #只回收用户空间的f,操作系统的文件还处于打开状态
  3.  
  4. #所以我们应该在del f之前保证f.close()执行,即便是没有del,程序执行完毕也会自动del清理资源,于是文件操作的正确用法应该是
  5. f=open('a.txt')
  6. 读写...
  7. f.close()
  8. 很多情况下大家都容易忽略f.close,这就用到了with上下文管理

  

十四 __enter__和__exit__

我们知道在操作文件对象的时候可以这么写

  1. with open('a.txt') as f:
  2.   '代码块'

  

上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法

上下文管理协议

  1. class Open:
  2. def __init__(self,name):
  3. self.name=name
  4.  
  5. def __enter__(self):
  6. print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
  7. # return self
  8. def __exit__(self, exc_type, exc_val, exc_tb):
  9. print('with中代码块执行完毕时执行我啊')
  10.  
  11. with Open('a.txt') as f:
  12. print('=====>执行代码块')
  13. # print(f,f.name)

  

__exit__()中的三个参数分别代表异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都无法执行

  1. class Open:
  2. def __init__(self,name):
  3. self.name=name
  4.  
  5. def __enter__(self):
  6. print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
  7.  
  8. def __exit__(self, exc_type, exc_val, exc_tb):
  9. print('with中代码块执行完毕时执行我啊')
  10. print(exc_type)
  11. print(exc_val)
  12. print(exc_tb)
  13.  
  14. with Open('a.txt') as f:
  15. print('=====>执行代码块')
  16. raise AttributeError('***着火啦,救火啊***')
  17. print('0'*100) #------------------------------->不会执行

  

如果__exit()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行

  1. class Open:
  2. def __init__(self,name):
  3. self.name=name
  4.  
  5. def __enter__(self):
  6. print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
  7.  
  8. def __exit__(self, exc_type, exc_val, exc_tb):
  9. print('with中代码块执行完毕时执行我啊')
  10. print(exc_type)
  11. print(exc_val)
  12. print(exc_tb)
  13. return True
  14.  
  15. with Open('a.txt') as f:
  16. print('=====>执行代码块')
  17. raise AttributeError('***着火啦,救火啊***')
  18. print('0'*100) #------------------------------->会执行

  

练习:模拟Open

  1. class Open:
  2. def __init__(self,filepath,mode='r',encoding='utf-8'):
  3. self.filepath=filepath
  4. self.mode=mode
  5. self.encoding=encoding
  6.  
  7. def __enter__(self):
  8. # print('enter')
  9. self.f=open(self.filepath,mode=self.mode,encoding=self.encoding)
  10. return self.f
  11.  
  12. def __exit__(self, exc_type, exc_val, exc_tb):
  13. # print('exit')
  14. self.f.close()
  15. return True
  16. def __getattr__(self, item):
  17. return getattr(self.f,item)
  18.  
  19. with Open('a.txt','w') as f:
  20. print(f)
  21. f.write('aaaaaa')
  22. f.wasdf #抛出异常,交给__exit__处理

  

用途或者说好处:

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

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

十五 __call__

  对象后面加括号,触发执行。

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

  1. class Foo:
  2.  
  3. def __init__(self):
  4. pass
  5.  
  6. def __call__(self, *args, **kwargs):
  7.  
  8. print('__call__')
  9.  
  10. obj = Foo() # 执行 __init__
  11. obj() # 执行 __call__
  1. # 结果:__call__

  

  1.  
  1. http://www.cnblogs.com/linhaifeng/articles/6204014.html#_label16此文参考上面博客,目的是巩固自己的知识点,方便自己学习

  

python 面向对象进阶之内置方法的更多相关文章

  1. python进阶之内置方法

    python进阶之内置方法 字符串类型的内置方法 常用操作与方法: 按索引取值 str[index] 切片 ste[start:stop:step] 长度 len(str) 成员运算in和not in ...

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

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

  3. python 面向对象的内置方法

    要求:了解即可,能用最好 """ 1.print(obj), str(obj), %s % (obj), 都调用obj.__str__()方法,若类中没有找__repr_ ...

  4. Python面向对象进阶(二)

    Python面向对象进阶2.html :first-child{margin-top:0!important}img.plugin{box-shadow:0 1px 3px rgba(0,0,0,.1 ...

  5. Python反射和内置方法(双下方法)

    Python反射和内置方法(双下方法) 一.反射 什么是反射 反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问.检测和修改它本身状态或行为的一种能力(自省).这一概念的提出很快引发 ...

  6. python常用数据类型内置方法介绍

    熟练掌握python常用数据类型内置方法是每个初学者必须具备的内功. 下面介绍了python常用的集中数据类型及其方法,点开源代码,其中对主要方法都进行了中文注释. 一.整型 a = 100 a.xx ...

  7. python字符串常用内置方法

    python字符串常用内置方法 定义: 字符串是一个有序的字符的集合,用与存储和表示基本的文本信息. python中引号中间包含的就是字符串. # s1='hello world' # s2=&quo ...

  8. python字符串处理内置方法一览表

    python字符串处理内置方法一览表   序号 方法及描述 1 capitalize()将字符串的第一个字符转换为大写 2 center(width, fillchar) 返回一个指定的宽度 widt ...

  9. Python面向对象之内置方法

    1.isinstance(obj,cls)和issubclass(sub,super) isinstance(obj,cls)检查是否obj是否是类 cls 的对象 issubclass(sub, s ...

随机推荐

  1. nslookup命令的安装及使用

    windows中是自带的,不用安装,直接在cmd窗口直接使用 Linux中需要安装: yum -y install bind-utils nslookup www.baidu.com [root@bo ...

  2. unity A*寻路 (二)读取NavMesh数据

    上一章节我们已经看了怎么获得NavMesh数据保存为obj 这一章节我们来读取NavMesh数据 首先我们要定义两个结构体 点 和 三角形 为什么不用unity自带的Vector3呢? 相信你们应该已 ...

  3. IPFS开发团队是如何工作的?

    小编不是一个很八卦的人,连当红明星都认不全.不过,今天还是带领大家来扒一扒ipfs开发团队是如何工作的. 工作方式: 全体会议:每周一有一个全体会议,这个会议是提前安排好的一个日程 任务讨论:把大任务 ...

  4. c++趣味之难以发现的bug

    这些bug可能够你喝一壶的. 1.被断言(assert)包含的代码 常发生在切换到release版本时,执行结果乖乖的,最终查找结果是assert括号里的代码在release下是不执行的. 发现:跟踪 ...

  5. shiro(二)自定义realm,模拟数据库查询验证

    自定义一个realm类,实现realm接口 package com; import org.apache.shiro.authc.*; import org.apache.shiro.realm.Re ...

  6. window7 安装sass和compass

    官网有详细的介绍,但是安装时候还是出现了一些小问题. 首先下载Rudy,然后根据提示勾选加入环境变量,由于第一次使用,我就选择了" msys2 base installlation" ...

  7. 排序算法Java实现(希尔排序)

    算法描述:先将待排序序列的数组元素分成多个子序列,使得每个子序列的元素个数相对较少,然后对各个子序列分别进行直接插入排序,待整个待排序序列“基本有序”后,再对所有元素进行一次直接插入排序. packa ...

  8. MySQL数据库学习三 数据库对象和基本操作

    3.1 数据库和数据对象 1. 系统数据库 information_schema:主要存储系统中的一些数据库对象信息,如用户列表信息.列信息.权限信息.字符集信息和分区信息等. performance ...

  9. IT连创业系列:产品设计之答题模块

    前言: 距上篇写完:IT连创业系列:新的一年,先淫文一篇! 转眼又两个星期了,今天不写文,估计大伙又得等两周了. 所以啊~~ 只能再努力一点了,花一天半天的,继续和大伙分享这让人心碎的创业历程. 这两 ...

  10. 造轮子-Java泛型堆排

    个人博客地址:http://kyle.org.cn/2018/03/13/heapsort/ Java实现泛型堆排算法,用于N个对象中选择最大或者最小的前M个,其中M<=N 类似于Mysql中o ...