15 内置方法(下)

面向对象学习目录

1 面向对象介绍

2 类、实例、属性、方法详解

3 面向过程与面向对象进一步比较

4 类与对象

5 属性查找与绑定方法

6 小结

7 继承与派生

8 组合

9 抽象类

10 多态

11 封装

12 绑定方法与非绑定方法

13 内置方法(上)

14 内置方法(中)之描述符

15 内置方法(下)

16 元类


七、再看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  
11 AAA=property(get_AAA,set_AAA,delete_AAA) #内置property三个参数与get,set,delete一一对应
12  
13 f1=Foo()
14 f1.AAA
15 f1.AAA='aaa'
16 del f1.AAA

怎么用?

案例一
 1 class Goods:
 2     def __init__(self):
 3         # 原价
 4         self.original_price = 100
 5         # 折扣
 6         self.discount = 0.8
 7  
 8     @property
 9     def price(self):
10         # 实际价格 = 原价 * 折扣
11         new_price = self.original_price * self.discount
12         return new_price
13  
14     @price.setter
15     def price(self, value):
16         self.original_price = value
17  
18     @price.deleter
19     def price(self):
20         del self.original_price
21  
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')
13 #property自动实现了set和get方法属于数据描述符,比实例属性优先级高,所以你这面写会触发property内置的set,抛出异常
14  
15  
16 #第二关:修订版
17  
18 class People:
19     def __init__(self,name):
20         self.name=name #实例化就触发property
21  
22     @property
23     def name(self):
24         # return self.name #无限递归
25         print('get------>')
26         return self.DouNiWan
27  
28     @name.setter
29     def name(self,value):
30         print('set------>')
31         self.DouNiWan=value
32  
33     @name.deleter
34     def name(self):
35         print('delete------>')
36         del self.DouNiWan
37  
38 p1=People('alex') #self.name实际是存放到self.DouNiWan里
39 print(p1.name)
40 print(p1.name)
41 print(p1.name)
42 print(p1.__dict__)
43  
44 p1.name='egon'
45 print(p1.__dict__)
46  
47 del p1.name
48 print(p1.__dict__)
49  
50  
51 #第三关:加上类型检查
52 class People:
53     def __init__(self,name):
54         self.name=name #实例化就触发property
55  
56     @property
57     def name(self):
58         # return self.name #无限递归
59         print('get------>')
60         return self.DouNiWan
61  
62     @name.setter
63     def name(self,value):
64         print('set------>')
65         if not isinstance(value,str):
66             raise TypeError('必须是字符串类型')
67             self.DouNiWan=value
68  
69     @name.deleter
70     def name(self):
71         print('delete------>')
72         del self.DouNiWan
73  
74 p1=People('alex') #self.name实际是存放到self.DouNiWan里
75 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 format_dict={
 2     'nat':'{obj.name}-{obj.addr}-{obj.type}',#学校名-学校地址-学校类型
 3     'tna':'{obj.type}:{obj.name}:{obj.addr}',#学校类型:学校名:学校地址
 4     'tan':'{obj.type}/{obj.addr}/{obj.name}',#学校类型/学校地址/学校名
 5     }
 6  
 7 class School:
 8     def __init__(self,name,addr,type):
 9         self.name=name
10         self.addr=addr
11         self.type=type
12  
13     def __repr__(self):
14         return 'School(%s,%s)' %(self.name,self.addr)
15     def __str__(self):
16         return '(%s,%s)' %(self.name,self.addr)
17  
18     def __format__(self, format_spec):
19         # if format_spec
20         if not format_spec or format_spec not in format_dict:
21             format_spec='nat'
22             fmt=format_dict[format_spec]
23             return fmt.format(obj=self)
24  
25 s1=School('oldboy1','北京','私立')
26 print('from repr: ',repr(s1))
27 print('from str: ',str(s1))
28 print(s1)
29  
30 '''
31 str函数或者print函数--->obj.__str__()
32 repr或者交互式解释器--->obj.__repr__()
33 如果__str__没有被定义,那么就会使用__repr__来代替输出
34 注意:这俩方法的返回值必须是字符串,否则抛出异常
35 '''
36 print(format(s1,'nat'))
37 print(format(s1,'tna'))
38 print(format(s1,'tan'))
39 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  
 7 class Date:
 8     def __init__(self,year,month,day):
 9         self.year=year
10         self.month=month
11         self.day=day
12  
13     def __format__(self, format_spec):
14         if not format_spec or format_spec not in date_dic:
15             format_spec='ymd'
16             fmt=date_dic[format_spec]
17             return fmt.format(self)
18  
19 d1=Date(2016,12,29)
20 print(format(d1))
21 print('{:mdy}'.format(d1))

issubclass和isinstance

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

 

十、slots

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

刨根问底

 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__) #f1与f2都没有属性字典__dict__了,统一归__slots__管,节省内存

 

十一、__next__和__iter__实现迭代器协议

简单示范
 1 class Foo:
 2     def __init__(self,x):
 3         self.x=x
 4  
 5     def __iter__(self):
 6         return self
 7  
 8     def __next__(self):
 9         n=self.x
10         self.x+=1
11         return self.x
12  
13 f=Foo(3)
14 for i in f:
15     print(i)
 1 class Foo:
 2     def __init__(self,start,stop):
 3         self.num=start
 4         self.stop=stop
 5     def __iter__(self):
 6         return self
 7     def __next__(self):
 8         if self.num >= self.stop:
 9             raise StopIteration
10         n=self.num
11         self.num+=1
12         return n
13  
14 f=Foo(1,5)
15  
16 from collections import Iterable,Iterator
17 print(isinstance(f,Iterator))
18  
19 for i in Foo(1,5): # obj=iter(Foo(1,5)) ---> 相当于Foo(1,5).__iter__()
20     print(i) # obj.__next__(),直到遇到StopIteration就自动终止(但for循环不会抛出异常报错)

练习:简单模拟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): # obj=iter(Range(1,7,3)) ---> 相当于Range(1,7,3).__iter__()
18     print(i) # obj.__next__(),直到遇到StopIteration就自动终止(但for循环不会抛出异常报错)

斐波那契数列

 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  
8 print(Bar.__doc__) #该属性无法继承给子类

 

十三、__module__和__class__

        __module__ 表示当前操作的对象在那个模块
        __class__ 表示当前操作的对象的类是什么
 
lib/aa.py
1 class C:
2     def __init__(self):
3         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     def __del__(self):
 3     print('执行我啦')
 4  
 5 f1=Foo()
 6 del f1
 7 print('------->')
 8  
 9 #输出结果
10 执行我啦
11 ------->

挖坑埋了你

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

 

典型的应用场景:
        创建数据库类,用该类实例化出数据库链接对象,对象本身是存放于用户空间内存中,而链接则是由操作系统管理的,存放于内核空间内存中
        当程序结束时,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     def __enter__(self):
 5         print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
 6         # return self
 7     def __exit__(self, exc_type, exc_val, exc_tb):
 8         print('with中代码块执行完毕时执行我啊')
 9  
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  
15 with Open('a.txt') as f:
16     print('=====>执行代码块')
17     raise AttributeError('***着火啦,救火啊***')
18     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  
16 with Open('a.txt') as f:
17     print('=====>执行代码块')
18         raise AttributeError('***着火啦,救火啊***')
19     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     def __init__(self):
 3         pass
 4  
 5     def __call__(self, *args, **kwargs):
 6         print('__call__')
 7  
 8  
 9 obj = Foo() # 执行 __init__
10 obj() # 执行 __call__

 

posted @ 2018-04-16 01:42  海上流星  阅读(354)  评论(0编辑  收藏  举报