面向对象进阶 |
一、isinstance(obj,cls)与issubclass(sub,super)
isinstance(obj,cls)检查是否obj是否是类cla的对象。具体代码如下所示:
class Foo: pass f1 = Foo() print(isinstance(f1,Foo)) #结果为:True
issubclass(sub,super)检查sub类是否是super类的派生类。具体代码如下:
class Foo: pass class Bar(Foo): pass print(issubclass(Bar,Foo)) #结果为:True
二、__getattribute__
我们在前面介绍过__getattr__的用法,当只有在使用点调用属性且属性不存在时候才会执行,那么__getattribute__不管存不存在都会执行,具体实现代码如下:
class Foo: def __init__(self,x): self.x = x def __getattribute__(self, item): print(‘执行的是getattribute‘) f1 = Foo(10) f1.x #结果为:执行的是getattribute f1.sss #结果为:执行的是getattribute
那么__getattr__与__getattribute__一起使用时,会先执行谁呢?我们通过一段代码来分析:
class Foo: def __init__(self,x): self.x = x def __getattr__(self, item): print(‘执行的是getattr‘) def __getattribute__(self, item): print(‘执行的是getattribute‘) f1 = Foo(10) f1.x #结果为:执行的是getattribute f1.sss #结果为:执行的是getattribute
从结果可以看出当__getattr__与__getattribute__一起使用时,只会执行__getattribute__。如果要执行__getattr__,除非__getattribute__在执行过程中抛出异常AttributeError。
三、类内置item函数
类内置item函数与之前学过的类内置attr函数用法十分相似,类内置attr函数是以点的方式调用,而类内置item函数以字典的方式调用
类内置item函数,分别是__getitem__、__setitem__、__delitem__。具体实现代码如下:
class Foo: def __getitem__(self, item): print(‘getitem‘) return self.__dict__[item] def __setitem__(self, key, value): print(‘setitem‘) self.__dict__[key] = value def __delitem__(self, key): print(‘delitem‘) self.__dict__.pop(key) f1 = Foo() print(f1.__dict__) #结果为:{} f1[‘name‘] = ‘alex‘ #结果为:setitem f1[‘age‘] = 18 #结果为:setitem print(f1.__dict__) #结果为:{‘name‘: ‘alex‘, ‘age‘: 18} del f1[‘name‘] #结果为:delitem print(f1.__dict__) #结果为:{‘age‘: 18} f1[‘age‘] #结果为:getitem
四、__str__与__repr__
这两个方法是改变对象的字符串显示,从字面意思就可以看出把输出的对象用于字符串显示。
我们来对以下代码来分析:
class Foo: def __init__(self,name,age): self.name = name self.age = age def __str__(self): return ‘名字是%s年龄是%s‘ %(self.name,self.age) def __repr__(self): return ‘这是repr‘ f1 = Foo(‘alex‘,18) print(f1) #结果为:名字是alex年龄是18
上述代码使用print操作时执行了类中的__str__方法,那么将__str__方法先注释,我们来看看到底print打印了什么?
class Foo: def __init__(self,name,age): self.name = name self.age = age def __repr__(self): return ‘这是repr‘ f1 = Foo(‘alex‘,18) print(f1) #结果为:这是repr‘
可以看出print操作执行了类中的__repr__方法,那么当__str__与__repr__一起使用时,一定是执行__str__吗?答案不是。下面就来介绍当什么情况的时候执行谁?
五、__format__
__format__可以自定制格式化字符串。具体实现如下:
format_dic = { ‘ymd‘ : ‘{0.year}{0.mon}{0.day}‘, ‘y:m:d‘ : ‘{0.year}:{0.mon}:{0.day}‘, ‘y-m-d‘ : ‘{0.year}-{0.mon}-{0.day}‘ } class Date(): def __init__(self,year,mon,day): self.year = year self.mon = mon self.day = day def __format__(self, format_spec): if not format_spec or format_spec not in format_dic: #不写格式或者不符合字典的格式,即生成默认格式 format_spec = ‘ymd‘ fm = format_dic[format_spec] return fm.format(self) d1 = Date(2019,7,30) print(format(d1)) print(format(d1,‘ymd‘)) print(format(d1,‘y-m-d‘)) print(format(d1,‘dgdg‘))
执行结果为:
2019730 2019730 2019-7-30 2019730
六、__slots__
__slots__是一个类变量,变量值可以是列表,元组,或者可迭代对象,也可以是一个字符串。
为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,单身有很多实例,为了节省内存可以使用__slots__取代实例的__dict__。
当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个。
class Foo: __slots__ = ‘name‘ f1 = Foo() f1.name = ‘alex‘ print(f1.name) #结果为:alex #print(f1.__dict__) #报错,因为类中定义了__slots__=‘name‘,所以name并没有存在属性字典中 print(f1.__slots__) #结果为:name
七、__doc__
__doc__是类的描述信息,该属性不能被继承。代码如下:
class Foo: ‘我是描述信息‘ pass class Bar(Foo): pass print(Foo.__doc__) #结果为:我是描述信息 print(Bar.__doc__) #结果为:None
八、__module__与__class__
__module__ 表示当前操作的对象在那个模块。
__class__表示当前操作的对象的类是什么。
例如aa.py文件下写如下代码:
class C: def __init__(self): self.name = ‘alex‘
在另一个文件下执行下面代码:
from aa import C print(c1.__module__) #结果为:aa print(c1.__class__) #结果为:<class ‘aa.C‘>
九、__del__
__del__被称为析构方法,当对象在内存中被释放时,自动触发执行。下面我们对两段代码进行分析:
第一段代码:
class Foo(): def __init__(self,name): self.name = name def __del__(self): print(‘我执行了‘) f1 = Foo(‘alex‘) print(‘-------------->‘)
执行结果为:
-------------->
我执行了
第二段代码:
class Foo(): def __init__(self,name): self.name = name def __del__(self): print(‘我执行了‘) f1 = Foo(‘alex‘) del f1 print(‘-------------->‘)
执行结果为:
我执行了
-------------->
上面两段代码为何输出结果相反?因为执行del了相当于提前释放对象。
十、__call__
__call__是指对象后面加括号,触发执行。
我们以前学过构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()。具体代码如下:
class Foo: def __call__(self, *args, **kwargs): print(‘实例执行了‘) f1 = Foo() f1() #结果为:实例执行了
十一、__iter__与__next__
这两个方法跟我们之前学的迭代器方法是一样的,它们就是迭代器协议。实现代码如下:
class Foo: def __init__(self,n): self.n = n def __iter__(self): return self def __next__(self): if self.n == 13: #当self.n等于13时抛出异常 raise StopIteration(‘终止了‘) self.n += 1 return self.n f1 = Foo(10) print(f1.__next__()) #结果为:11 print(f1.__next__()) #结果为:12 print(next(f1)) #结果为:13 print(next(f1)) #抛出异常
利用迭代器协议实现斐波那契数列,具体实现代码如下:
class Fib: def __init__(self): self._a = 1 self._b = 1 def __iter__(self): return self def __next__(self): if self._a > 10: raise StopIteration(‘终止‘) self._a,self._b = self._b,self._a+self._b return self._a f1 = Fib() print(next(f1)) print(next(f1)) print(‘-------------->‘) for i in f1: print(i)
执行结果为:
1 2 --------------> 3 5 8 13
原文:https://www.cnblogs.com/lzc69/p/11310279.html