Python之面向对象进阶系列

一、反射(hasattr/getattr/setattr/delattr

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

分别表示:判断对象有没有对应的方法以及属性   获取属性  设置属性  删除属性

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 
 5 class BlackMedium:
 6 
 7     def __init__(self, name, addr):
 8         self.name = name
 9         self.addr = addr
10 
11     def sale_house(self):
12         print('【%s】正在卖房子,煞笔才买呢?' % self.name)
13 
14     def rent_house(self):
15         print('【%s】正在租房子,煞笔才租呢?' % self.name)
16 
17 
18 b1 = BlackMedium('万科置地', '锦江花园路')
19 # hasattr
20 # print(hasattr(b1, 'name'))
21 # print(hasattr(b1, 'rent_house'))
22 # print(b1.rent_house)
23 # func = b1.rent_house
24 # func()
25 
26 # getattr
27 # print(getattr(b1, 'name'))
28 # print(getattr(b1, 'sale_house'))  # == b1.sale_house
29 # print(getattr(b1, 'sale_house1111111111'))  # 没有就会报错
30 # print(getattr(b1, 'sale_house1111', '没有这个属性'))  # default=None 加上默认就不会报错了
31 
32 
33 # setattr
34 # 设置对象值 b1.sb = True
35 # setattr(b1, 'sb', True)
36 # setattr(b1, 'sb1', 123)
37 # setattr(b1, 'name', 'SB')
38 # 加函数属性
39 # setattr(b1, 'func', lambda self: self.name + 'SB')
40 # print(b1.__dict__)
41 # print(b1.func(b1))
42 
43 # delattr
44 # del b1.sb
45 # del b1.sb1
46 # print(b1.__dict__)
47 # delattr(b1, 'sb')
48 # print(b1.__dict__)

总结:

hasattr(obj,'属性') #obj.属性 是否存在
getattr(obj,'属性') #获取obj.属性 不存在则报错
getattr(obj,'属性','默认值') #获取obj.属性 不存在不会报错,返回那个默认值
setattr(obj,'属性','属性的值') #obj.属性=属性的值
delattr(obj,'属性') #del obj.属性

二、双下划线attr

__setattr__   添加/修改属性会触发它的执行

__delattr__   删除属性的时候会触发

__getattr__   只有在使用点调用属性且属性不存在的时候才会触发

作用:系统内置函数属性(你定义了就用你定义的函数属性,不定义就用系统默认的函数属性)

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 
 5 class Foo:
 6     x = 1
 7 
 8     def __init__(self, y):
 9         self.y = y
10 
11     def __getattr__(self, item):
12         print('执行__getattr__')
13 
14     def __delattr__(self, item):
15         print('执行__delattr__')
16         # self.__dict__.pop(item)
17 
18     def __setattr__(self, key, value):
19         print('执行__setattr__')
20         # self.key = value   # RecursionError: maximum recursion depth exceeded 无限死递归超过最大层数了
21         # self.__dict__[key] = value  # 直接在修改底层字典
22 
23 
24 # __getattr__方法
25 f = Foo(10)
26 # print(f.y)
27 # print(getattr(f, 'y'))
28 print(f.z)   # 调用对象不存在属性的时候就触发__getattr__方法
29 
30 
31 # __delattr__方法
32 # del f.y     # 触发__delattr__方法执行
33 
34 
35 # __setattr__方法  设置属性的时候就会触发__setattr__
36 print(f.__dict__)
37 f.v = 2
38 setattr(f, 'z', 1)
39 print(f.__dict__)
40 print(dir(Foo))

总结: 

obj点的方式去操作属性时触发的方法

__getattr__:obj.属性 不存在时触发
__setattr__:obj.属性=属性的值 时触发
__delattr__:del obj.属性 时触发

三、继承方式完成包装标准类型

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

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 
 5 class List(list):
 6     def show_midlle(self):
 7         mid_index = int(len(self)/2)
 8         return self[mid_index]
 9 
10     def append(self, p_obj):
11         if type(p_obj) is str:
12             # list.append(self, p_obj)
13             super().append(p_obj)
14         else:
15             print('必须添加字符串类型的')
16 
17 
18 l1 = List('helloworld')
19 print(l1.show_midlle())
20 print(l1)
21 l1.append(11111)
22 l1.append('sb')
23 print(l1)

四、授权 

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

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

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 import time
 4 
 5 
 6 class FileHandler:
 7     def __init__(self, filename, mode='r', encoding='utf-8'):
 8         self.file = open(filename, mode, encoding=encoding)
 9         self.mode = mode
10         self.encoding = encoding
11 
12     def write(self, line):
13         t = time.strftime('%Y-%m-%d %X')
14         self.file.write('%s %s' % (t, line))
15 
16     def __getattr__(self, item):
17         # print('----->', item)
18         return getattr(self.file, item)
19 
20 
21 f = FileHandler('a.txt', 'r+')
22 print(f.read())  # 先找自己  然后找类 类还是没有就触发__getattr__
23 f.write('111111
')
24 f.write('222222
')
25 f.write('333333
')

五、isinstance  issubclass

一个对象是否是一个类的对象

判断一个类是否是另外一个类的子类

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 
 5 class Foo:
 6     pass
 7 
 8 
 9 class Bar(Foo):
10     pass
11 
12 
13 # 一个对象是否是一个类的对象
14 f = Foo()
15 print(isinstance(f, Foo))
16 
17 # 判断一个类是否是另外一个类的子类
18 print(issubclass(Bar, Foo))

 六、__getattr__ 、__getattribute__区别

不管存不存在都要去执行__getattribute__,它是大哥

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 
 5 class Foo:
 6     def __init__(self, x):
 7         self.x = x
 8 
 9     def __getattr__(self, item):
10         print('执行__getattr__', item)
11 
12     def __getattribute__(self, item):
13         # print('执行__getattribute__', item)
14         print('不管是否存在我都要跑起来!')
15         raise AttributeError('哈哈')
16 
17 
18 f = Foo(10)
19 f.x
20 f.xxxx
21 # 当二者__getattr__ __getattribute__同时存在,
22 # 只会执行__getattribute__,除非抛出raise AttributeError()才去执行__getattr__

 七、item系列(操作字典就用item的方式)

obj[‘属性’]的方式去操作属性时触发的方法

__getitem__:obj['属性'] 时触发
__setitem__:obj['属性']=属性的值 时触发
__delitem__:del obj['属性'] 时触发

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 
 5 class Foo:
 6     def __getitem__(self, item):
 7         print('执行__getitem__')
 8         return self.__dict__[item]
 9 
10     def __setitem__(self, key, value):
11         print('执行__setitem__')
12         self.__dict__[key] = value
13 
14     def __delitem__(self, key):
15         print('执行__delitem__')
16         self.__dict__.pop(key)
17 
18 
19 f = Foo()
20 print(f.__dict__)
21 # f.name = 'egon' #.的方式是调用的attr哪些方法 没调用setitem
22 f['name'] = 'egon'
23 f['age'] = 18
24 print(f.__dict__)
25 
26 # del f.age   # 通过字典的方式删才会执行delitem
27 # del f['age']
28 # print(f.__dict__)
29 
30 print(f.age)      # 执行的attr哪些方法
31 print(f['age'])  # 执行的是getitem

八、__str__、__repr__

改变对象的字符串显示__str__,__repr__  (只能是字符串的值,不能是非字符串的值)

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 
 5 class Foo:
 6     def __init__(self, name):
 7         self.name = name
 8 
 9     def __str__(self):
10         ret = '%s' % self.name
11         return ret
12         # 这里不能返回其它类型的值只能返回字符串类型
13 
14     def __repr__(self):
15         ret = '%s' % self.name
16         return ret
17 
18 
19 f = Foo('egon')
20 print(f)   # 不定义__str__就返回<__main__.Foo object at 0x0000021F398281D0>  定义之后的返回值egon
21 # str(f) ------->f.__str__()   print实际是触发系统的__str__方法
22 print(str(f))
23 print(f.__str__())
24 
25 # repr(f) ------->f.__repr__() 在终端解释器操作返回的结果
26 # 共存的情况下 print本质就是调用__str__, 如果没有__str__,就去找__repr__(替代品)

总结: 

str函数或者print函数--->obj.__str__()
repr或者交互式解释器--->obj.__repr__()
如果__str__没有被定义,那么就会使用__repr__来代替输出
注意:这俩方法的返回值必须是字符串,否则抛出异常 

九、类变量  (__slots__) 

__slots__的作用:节省内存空间

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

十、__doc__

1 类中的文档信息 但是不能继承

十一、__module__  、__class__

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

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

1.创建lib/aa.py  2.输出模块和输出类

1 class C:
2 
3     def __init__(self):
4         self.name = 'SB'
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是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 
 5 """
 6 析构方法,当对象在内存中被释放时,自动触发执行。
 7 
 8 注:如果产生的对象仅仅只是python程序级别的(用户级),
 9 那么无需定义__del__,如果产生的对象的同时还会向操作系统发起系统调用,
10 即一个对象有用户级与内核级两种资源,比如(打开一个文件,创建一个数据库链接),
11 则必须在清除对象的同时回收系统资源,这就用到了__del__
12 """
13 
14 
15 class Foo:
16     def __init__(self, name):
17         self.name = name
18 
19     def __del__(self):
20         print('我开始执行')
21 
22 
23 f1 = Foo('egon')
24 # del f1.name  删属性
25 del f1         # 删对象 垃圾回收机制 释放掉f1对象
26 print('-------------------------')

十三、__call__方法

对象后面加括号就触发call方法的运行

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 
 5 """
 6 对象后面加括号,触发执行。
 7 
 8 注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
 9 """
10 
11 
12 class Foo:
13     def __init__(self):
14         pass
15 
16     def __call__(self, *args, **kwargs):
17         print('我开始执行了')
18 
19 
20 f1 = Foo()
21 print(dir(Foo))
22 print(f1)
23 
24 f1()  # 触发call 方法

 十四、元类(metaclass)

元类是类的类,是类的模板

元类是用来控制如何创建类的,正如类是创建对象的模板一样

元类的实例为类,正如类的实例为对象(f1对象是Foo类的一个实例Foo类是 type 类的一个实例)

type是python的一个内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类实例化的对象

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 
 5 class MyType(type):
 6 
 7     def __init__(self, a, b, c):
 8         print('元类的构造方法')
 9         print(a)   # Foo
10         print(b)   # ()
11         print(c)   # {'__module__': '__main__', '__qualname__': 'Foo', '__init__': <function Foo.__init__ at 0x0000024FBD091598>}
12 
13     def __call__(self, *args, **kwargs):
14         obj = object.__new__(self)   # object.__new__(Foo) --->f1
15         self.__init__(obj, *args, **kwargs)  # Foo.__init__(f1, *args, **kwargs)
16         return obj
17 
18 
19 class Foo(metaclass=MyType):  # Foo=MyType(Foo,'Foo',(),{}) ----->__init__
20     def __init__(self, name):
21         self.name = name
22 
23 
24 f1 = Foo('alex')
25 print(f1.__dict__)

 十五、描述符(__get__,__set__,__delete__) 

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

 1 class 描述符:
 2     def __get__():
 3         pass
 4     def __set__():
 5         pass
 6     def __delete__():
 7         pass
 8 
 9 class 类:
10     name=描述符()
11 
12 obj=类()
13 obj.name        #get方法
14 obj.name='egon' #set方法
15 del obj.name    #delete

描述符的三种方法:

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

1.定义一个描述符

1 class Foo:   #在python3中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方法')

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

 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 print(f1.name)
13 del f1.name
14 #疑问:何时,何地,会触发这三个方法的执行

3、描述符应用之何时?何地?

 1 #描述符应用之何时?何地?
 2 
 3 #描述符Str
 4 class Foo:
 5     def __get__(self,instance,owner):
 6         print('===>get方法')
 7     def __set__(self, instance, value):
 8         print('===>set方法')
 9     def __delete__(self, instance):
10         print('===>delete方法')
11 
12 
13 class Bar:
14     x=Foo() #在何地?
15 
16 print(Bar.__dict__)
17 
18 #在何时?
19 b1=Bar()
20 # b1.x                 #调用就会触发上面的get方法
21 # b1.x=1               #赋值
22 # del b1.x
23 print(b1.x)            #触发了描述器里面的get方法,得到None
24 b1.x=1                 #触发了描述器里面的set方法,得到{}
25 print(b1.__dict__)     #写到b1的属性字典中
26 
27 del b1.x               #打印===>delete方法

执行结果:

 1 {'x': <__main__.Foo object at 0x014210D0>, '__weakref__': <attribute '__weakref__' of 'Bar' objects>, '__dict__': <attribute '__dict__' of 'Bar' objects>, '__doc__': None, '__module__': '__main__'}
 2 
 3 ===>get方法
 4 
 5 None
 6 
 7 ===>set方法
 8 
 9 {}
10 
11 ===>delete方法
View Code

4、描述符分两种

一、数据描述符:至少实现了__get__()和__set__()两种方法

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

5、注意事项:
一、描述符本身应该定义成新式类,被代理的类也应该是新式类
二、必须把描述符定义成另外一个类触发的类属性,不能为定义到构造函数中

 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 class Bar:
10     x=Foo() #定义一个描述符
11     def __init__(self,n):
12         self.x=n
13 
14 b1=Bar(10)   #触发set方法
15 print(b1.__dict__)

执行结果:

1 ===>set方法
2 {}

三、要严格遵循该优先级,优先级由高到底分别是
1.类属性
2.数据描述符
3.实例属性
4.非数据描述符
5.找不到的属性触发__getattr__()

类属性>数据描述符

 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 class Bar:
10     x=Foo()   #调用foo()属性,会触发get方法
11 
12 print(Bar.x)  #类属性比描述符有更高的优先级,会触发get方法
13 Bar.x=1       #自己定义了一个类属性,并赋值给x,跟描述符没有关系,所以他不会触发描述符的方法
14 # print(Bar.__dict__)
15 print(Bar.x)

执行结果:

1 ===>get方法
2 None
3 1

数据描述符>实例属性

例子1:

 1 #有get,set就是数据描述符,数据描述符比实例属性有更高的优化级
 2 
 3 class Foo:
 4     def __get__(self,instance,owner):
 5         print('===>get方法')
 6     def __set__(self, instance, value):
 7         print('===>set方法')
 8     def __delete__(self, instance):
 9         print('===>delete方法')
10 
11 class Bar:
12     x = Foo()  # 调用foo()属性,会触发get方法
13 
14 b1=Bar()   #在自己的属性字典里面找,找不到就去类里面找,会触发__get__方法
15 b1.x       #调用一个属性的时候触发get方法
16 b1.x=1     #为一个属性赋值的时候触发set方法
17 del b1.x   #采用del删除属性时触发delete方法

 执行结果:

1 ===>get方法
2 ===>set方法
3 ===>delete方法

 例子2

 1 class Foo:
 2     def __get__(self,instance,owner):
 3         print('===>get方法')
 4 
 5     def __set__(self, instance, value):
 6         pass
 7 
 8 class Bar:
 9     x = Foo()
10 
11 b1=Bar()
12 b1.x=1        #触发的是非数据描述符的set方法
13 print(b1.__dict__)

执行结果:

1 {}   #数据描述符>实例属性

类属性>数据描述符>实例属性

 1 #类属性>数据描述符>实例属性
 2 
 3 class Foo:
 4     def __get__(self,instance,owner):
 5         print('===>get方法')
 6     def __set__(self, instance, value):
 7         print('===>set方法')
 8     def __delete__(self, instance):
 9         print('===>delete方法')
10 
11 class Bar:
12     x = Foo()             #调用foo()属性,会触发get方法
13 
14 b1=Bar()                  #实例化
15 Bar.x=11111111111111111   #不会触发get方法
16 b1.x                      #会触发get方法
17 
18 del Bar.x                 #已经给删除,所以调用不了!报错:AttributeError: 'Bar' object has no attribute 'x'
19 b1.x

非数据描述符

例子1:

 1 #非数据描述符没有set方法
 2 class Foo:
 3     def __get__(self,instance,owner):
 4         print('===>get方法')
 5 
 6     def __delete__(self, instance):
 7         print('===>delete方法')
 8 
 9 class Bar:
10     x = Foo()
11 
12 b1=Bar()
13 b1.x     #自己类中没有,就会去Foo类中找,所以就会触发__get__方法

执行结果:

===>get方法

例子2:

 1 #实例属性>非数据描述符
 2 class Foo:
 3     def __get__(self,instance,owner):
 4         print('===>get方法')
 5 
 6 class Bar:
 7     x = Foo()
 8 
 9 b1=Bar()
10 b1.x=1
11 print(b1.__dict__)  #在自己的属性字典里面,{'x': 1}

执行结果:

1 {'x': 1}

 

非数据描述符>找不到

 1 #非数据描述符>找不到
 2 
 3 class Foo:
 4     def __get__(self,instance,owner):
 5         print('===>get方法')
 6 
 7 class Bar:
 8     x = Foo()
 9     def __getattr__(self, item):
10         print('------------>')
11 
12 b1=Bar()
13 b1.xxxxxxxxxxxxxxxxxxx    #调用没有的xxxxxxx,就会触发__getattr__方法

执行结果:

1 ------------>    #解发__getattr__方法

 单下划线开头的变量
  “单下划线”开始的变量叫做保护变量,意思是只有类对象和子类对象能访问到这些变量

双下划线开头的变量
“双下划线”开始的变量叫做私有变量,意思是只有类本身能够访问达到这些变量,连子类对象也不能访问。

对象名._类名__PrivateMethod()

单下划线与双下划线的区别

https://www.cnblogs.com/wang-kai-xuan/p/11407832.html

原文地址:https://www.cnblogs.com/Alexephor/p/11215644.html