python学习——面对对象进阶
一、isinstance和issubclass
isinstance(obj,cls)检查是否obj是否是类 cls 的对象
1 class Foo: 2 pass 3 4 a = Foo() 5 print(isinstance(Foo,object)) 6 7 # 输出:True 8 9 class Parent(object): 10 pass 11 12 b = Parent() 13 print(isinstance(Parent,object)) 14 15 # 输出:True
由此可以看出,python3中若没有说明继承的是哪个类的时候,默认继承object。
issubclass(sub, super)检查sub类是否是 super 类的派生类
class Parent(): pass class Son(Parent): pass print(issubclass(Parent,object)) print(issubclass(Son,Parent)) # 输出: True True
二、反射
1、什么是反射(非常强大,也很重要)
反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。
2 python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)
四个可以实现自省的函数
下列方法适用于类和对象(一切皆对象,类本身也是一个对象)
1 def hasattr(*args, **kwargs): # real signature unknown 2 """ 3 Return whether the object has an attribute with the given name. 4 5 This is done by calling getattr(obj, name) and catching AttributeError. 6 """ 7 pass
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 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 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 Parent(): 2 p = '类的静态变量' 3 def __init__(self,name,age,sex): 4 self.name = name 5 self.age = age 6 self.sex = sex 7 8 def say_hello(self): 9 return '{}say {}'.format(self.name,'hello') 10 11 obj = Parent('周曾吕',88,'不详') 12 13 # 检测是否含有某个属性 14 print(hasattr(obj,'name')) 15 print(hasattr(obj,'age')) 16 print(hasattr(obj,'sex')) 17 print(hasattr(obj,'say_hello')) 18 19 # 输出: 20 True 21 True 22 True 23 True 24 25 # 获取属性 26 n1 = getattr(obj,'name') 27 n2 = getattr(obj,'age') 28 n3 = getattr(obj,'sex') 29 30 print(n1,n2,n3) 31 32 # 输出:周曾吕 88 不详 33 34 func = getattr(obj,'say_hello') 35 print(func) 36 37 # 输出: 38 <bound method Parent.say_hello of <__main__.Parent object at 0x00000222F34B2CF8>> 39 40 # print(getattr(obj,'aaaa','不存在')) # 不存在 报错 41 42 # 设置属性(赋值) 43 setattr(obj,'sb',True) 44 setattr(obj,'show_name',lambda self:self.name + 'sb') 45 print(obj.__dict__) 46 print(obj.show_name(obj)) 47 48 # 输出: 49 {'name': '周曾吕', 'age': 88, 'sex': '不详', 'sb': True, 'show_name': <function <lambda> at 0x00000222F34CD730>} 50 周曾吕sb 51 52 # 删除属性 53 delattr(obj,'sex') 54 delattr(obj,'show_name') 55 delattr(obj,'sb') 56 # delattr(obj,'show_name_aaa') # 不存在就报错 57 58 print(obj.__dict__) 59 60 # 输出: 61 {'name': '周曾吕', 'age': 88}
1 class Foo: 2 3 staticName = 'University Guiyang' 4 5 def __init__(self): 6 self.name = 'liulonghai' 7 8 def func(self): 9 return 'in func now ' 10 11 @staticmethod 12 def qqxing(): 13 return 'qqxing' 14 15 print(getattr(Foo,'staticName')) 16 print(getattr(Foo,'func')) 17 print(getattr(Foo,'qqxing')) 18 19 ret1 = getattr(Foo,'func') 20 ret2 = getattr(Foo,'qqxing') 21 22 print(ret1(Foo)) 23 print(ret2()) 24 25 # 输出: 26 University Guiyang 27 <function Foo.func at 0x0000020D5570E8C8> 28 <function Foo.qqxing at 0x0000020D5570E950> 29 in func now 30 qqxing
注意:当调用类里面的普通方法时,要传一个类名作为参数,否则会报错。如上面的 ret2(Foo)
1 import sys 2 3 n1 = 'qqxing' 4 def func1(): 5 return 'in func1 now' 6 7 n2 = 'wahaha' 8 def func2(): 9 return 'in func2 now' 10 11 the_module = sys.modules[__name__] 12 13 if hasattr(the_module,'func1'): 14 ret1 = getattr(the_module,'func1') 15 print(ret1()) 16 if hasattr(the_module,'func2'): 17 ret2 = getattr(the_module,'func2') 18 print(ret2()) 19 20 print(getattr(the_module,'n1')) 21 print(getattr(the_module,'n2')) 22 23 # 输出: 24 in func1 now 25 in func2 now 26 qqxing 27 wahaha
注意:不管是函数还是变量,反射的时候只能有一个变量,且变量必须是全局变量。
导入其他模块,可以利用反射来运行该模块中存在的方法
1 import 面向对象的三大特性 2 3 if hasattr(面向对象的三大特性,'A'): 4 ret1 = getattr(面向对象的三大特性,'A') 5 print(ret1) 6 print(ret1()) 7 8 # 输出: 9 <class '面向对象的三大特性.A'> 10 China
注意,若导入的模块是一个类而不是函数,运行的时候可能会报错,他会说这个类的类名不可调用,是因为这个类中没有__call__这个方法,如果你加上之后就不会报错了,但是他返回的是一个内存地址,没有显示这个函数的结果(不管你怎么调用都不显示,而且不会报错,是不是懵逼了),其实是因为类中没有实现__repr__或者__str__这两个双下方法中的其中一个,你加上就可以了。。。
三、__repr__和__str__
改变对象的字符串显示__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 class School: 7 def __init__(self,name,addr,type): 8 self.name = name 9 self.addr = addr 10 self.type = type 11 12 def __repr__(self): 13 return '学校名:{} 地址:{}'.format(self.name,self.addr) 14 def __str__(self): 15 return '学校名:{} 地址:{}'.format(self.name,self.addr) 16 17 def __format__(self, format_spec): 18 # if format_spec 19 if not format_spec or format_spec not in format_dict: 20 format_spec = 'nat' 21 fmt = format_dict[format_spec] 22 return fmt.format(obj=self) 23 24 s1=School('贵阳学院','贵阳','公立') 25 print('from repr: ', repr(s1)) 26 print('from str: ', str(s1)) 27 print(s1) 28 29 ''' 30 str函数或者print函数--->obj.__str__() 31 repr或者交互式解释器--->obj.__repr__() 32 如果__str__没有被定义,那么就会使用__repr__来代替输出 就是说__repr__是__str__的备胎 33 注意:这俩方法的返回值必须是字符串,否则抛出异常 34 ''' 35 print(format(s1, 'nat')) 36 print(format(s1, 'tna')) 37 print(format(s1, 'tan')) 38 print(format(s1, 'asfdasdffd')) 39 40 # 输出: 41 from repr: 学校名:贵阳学院 地址:贵阳 42 from str: 学校名:贵阳学院 地址:贵阳 43 学校名:贵阳学院 地址:贵阳 44 贵阳学院-贵阳-公立 45 公立:贵阳学院:贵阳 46 公立/贵阳/贵阳学院 47 贵阳学院-贵阳-公立
1 class A: 2 def __str__(self): 3 return 'from str: class A' 4 5 def __repr__(self): 6 return 'from repr: class A' 7 8 b = A() 9 print('%s'%b) 10 print('%r'%b) 11 12 # 输出: 13 from str: class A 14 from repr: class A
四、__del__
析构方法,当对象在内存中被释放时,自动触发执行。
注意:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
1 class A: 2 def __del__(self): 3 print('执行我呗!') 4 5 a = A() 6 del a 7 print('*******') 8 9 # 输出: 10 执行我呗! 11 *******
五、item系列
5.1、__getitem__ __setitem__ __delitem__
1 class A: 2 def __init__(self,name): 3 self.name = name 4 5 def __getitem__(self, item): 6 return self.__dict__[item] 7 8 def __setitem__(self, key, value): 9 self.__dict__[key] = value 10 11 def __delitem__(self, key): 12 print('*******del obj[key]时,执行我啦********') 13 self.__dict__.pop(key) 14 15 def __delattr__(self, item): 16 print('del obj[key]时,执行我啦') 17 self.__dict__.pop(item) 18 19 a = A('小周哥') 20 a['age1'] = 18 21 a['age2'] = 88 22 print(a.__dict__) 23 del a.age2 24 del a['age1'] 25 a['name'] = '小伟哥' 26 print(a.__dict__) 27 28 # 输出: 29 {'name': '小周哥', 'age1': 18, 'age2': 88} 30 del obj[key]时,执行我啦 31 *******del obj[key]时,执行我啦******** 32 {'name': '小伟哥'}
六、__new__
1 class A: 2 def __init__(self): 3 self.x = 1000000 4 print('in init function now ') 5 6 def __new__(cls, *args, **kwargs): 7 print('in new function now ') 8 return object.__new__(A,*args,**kwargs) 9 10 a = A() 11 print(a.x) 12 13 # 输出: 14 in new function now 15 in init function now 16 1000000
1 class Singleton: 2 def __new__(cls, *args, **kwargs): 3 if not hasattr(cls,'_instance'): 4 cls._instance = object.__new__(cls,*args,**kwargs) 5 6 return cls._instance 7 8 one = Singleton() 9 one.name = '刘龙海' 10 two = Singleton() 11 two.name = '刘永贵' 12 print(one.name) 13 # 刘永贵 14 print(id(one)) 15 # 3237255572616 16 print(id(two)) 17 # 3237255572616 18 print(one.name) 19 # 刘永贵 20 print(one == two) 21 # True 22 print(one is two) 23 # True
单例模式:在类中始终都只有一个实例。
1 class A: 2 def __new__(cls, *args, **kwargs): 3 if not hasattr(cls,'_instance'): 4 cls._instance = object.__new__(A,*args,**kwargs) 5 return cls._instance 6 7 a = A() 8 aa = A() 9 aa.name = '刘永贵' 10 a.name = '刘龙海' 11 print(a.name) 12 # 刘龙海 13 print(aa.name) 14 # 刘龙海 15 print(id(a)) 16 # 2266637659216 17 print(id(aa)) 18 # 2266637659216 19 print(a == aa) 20 # True 21 print(a is aa ) 22 # True
七、__call__
对象后面加括号,触发执行。
注意:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
1 class A: 2 def __init__(self): 3 pass 4 5 def __call__(self, *args, **kwargs): 6 return 'in __call__' 7 8 a = A() 9 print(a()) 10 print(A()()) 11 12 # 输出: 13 in __call__ 14 in __call__
八、__len__
1 class A: 2 def __init__(self): 3 self.a = 1 4 self.aa = 2 5 6 def __len__(self): 7 return len(self.__dict__) 8 9 aaa = A() 10 print(len(aaa)) 11 12 # 输出:2
九、__hash__
1 class A: 2 def __init__(self): 3 self.a = 1 4 self.aa = 2 5 6 def __hash__(self): 7 return hash(str(self.a)+str(hash(self.aa))) 8 9 aaa = A() 10 print(hash(aaa)) 11 12 # 输出:5078281282522828503
十、__eq__
1 class A: 2 def __init__(self): 3 self.a = 1 4 self.aa = 2 5 6 def __eq__(self, other): 7 if self.a == other.a and self.aa == other.aa: 8 return True 9 10 c1 = A() 11 c2 = A() 12 print(c1 == c2) 13 14 # 输出:True
1 class A: 2 def __init__(self): 3 self.a = 1 4 self.aa = 2 5 6 def __eq__(self, other): 7 if self.a == other.a and self.aa == other.aa: 8 return True 9 10 a = A() 11 aa = A() 12 print(a == aa) 13 14 # 输出:True
十一、面向对象进阶实例
1.扑克牌游戏
1 from collections import namedtuple 2 from random import choice,shuffle 3 import json 4 5 Card = namedtuple('牌',['num_types','numbers']) 6 class Pukepai(): 7 numbers = [str(n) for n in range(2,11)] + list('JQKA') 8 num_types = ['黑桃','红桃','梅花','方块'] 9 10 def __init__(self): 11 self._cards = [Card(num_type,number) for num_type in Pukepai.num_types 12 for number in Pukepai.numbers] 13 14 def __call__(self, *args, **kwargs): 15 return Pukepai 16 17 def __len__(self): 18 return len(self._cards) 19 20 def __getitem__(self, item): 21 return self._cards[item] 22 23 def __setitem__(self, key, value): 24 self._cards[key] = value 25 26 def __str__(self): 27 return json.dumps(self._cards,ensure_ascii=False) 28 29 puke = Pukepai() 30 print(puke[10]) 31 print(choice(puke)) 32 print(choice(puke)) 33 print(choice(puke)) 34 print(choice(puke)) 35 print(choice(puke)) 36 shuffle(puke) 37 print(puke[:52]) 38 39 # 输出: 40 牌(num_types='黑桃', numbers='Q') 41 牌(num_types='方块', numbers='Q') 42 牌(num_types='方块', numbers='4') 43 牌(num_types='红桃', numbers='8') 44 牌(num_types='红桃', numbers='3') 45 牌(num_types='梅花', numbers='6') 46 [牌(num_types='红桃', numbers='J'), 牌(num_types='红桃', numbers='8'), 牌(num_types='方块', numbers='2'), 牌(num_types='黑桃', numbers='7'), 牌(num_types='红桃', numbers='2'), 牌(num_types='方块', numbers='A'), 牌(num_types='梅花', numbers='3'), 牌(num_types='方块', numbers='7'), 牌(num_types='梅花', numbers='8'), 牌(num_types='方块', numbers='K'), 牌(num_types='黑桃', numbers='Q'), 牌(num_types='方块', numbers='3'), 牌(num_types='红桃', numbers='7'), 牌(num_types='梅花', numbers='5'), 牌(num_types='方块', numbers='8'), 牌(num_types='红桃', numbers='A'), 牌(num_types='黑桃', numbers='J'), 牌(num_types='梅花', numbers='10'), 牌(num_types='黑桃', numbers='K'), 牌(num_types='红桃', numbers='10'), 牌(num_types='方块', numbers='4'), 牌(num_types='梅花', numbers='Q'), 牌(num_types='梅花', numbers='7'), 牌(num_types='黑桃', numbers='3'), 牌(num_types='方块', numbers='5'), 牌(num_types='红桃', numbers='4'), 牌(num_types='方块', numbers='10'), 牌(num_types='黑桃', numbers='4'), 牌(num_types='红桃', numbers='9'), 牌(num_types='梅花', numbers='4'), 牌(num_types='红桃', numbers='3'), 牌(num_types='黑桃', numbers='A'), 牌(num_types='梅花', numbers='K'), 牌(num_types='黑桃', numbers='8'), 牌(num_types='黑桃', numbers='5'), 牌(num_types='方块', numbers='J'), 牌(num_types='方块', numbers='Q'), 牌(num_types='红桃', numbers='6'), 牌(num_types='梅花', numbers='J'), 牌(num_types='黑桃', numbers='6'), 牌(num_types='梅花', numbers='2'), 牌(num_types='梅花', numbers='A'), 牌(num_types='黑桃', numbers='2'), 牌(num_types='梅花', numbers='9'), 牌(num_types='红桃', numbers='Q'), 牌(num_types='黑桃', numbers='9'), 牌(num_types='方块', numbers='9'), 牌(num_types='梅花', numbers='6'), 牌(num_types='黑桃', numbers='10'), 牌(num_types='红桃', numbers='K'), 牌(num_types='方块', numbers='6'), 牌(num_types='红桃', numbers='5')]
2.一道面试题
1 class Person: 2 def __init__(self,name,sex,age): 3 self.name = name 4 self.age = age 5 self.sex = sex 6 7 def __hash__(self): 8 return hash(self.name + self.sex) 9 10 def __eq__(self, other): 11 if self.name == other.name and self.sex == other.sex: 12 return True 13 14 p_lst = [] 15 for i in range(10): 16 p_lst.append(Person('刘龙海','男',i)) 17 18 print(p_lst[:2]) 19 print(set(p_lst)) 20 21 # 输出: 22 [<__main__.Person object at 0x0000023940FF5B38>, <__main__.Person object at 0x0000023940FF5C88>] 23 {<__main__.Person object at 0x0000023940FF5B38>}