python 面向对象的进阶
isinstance和issubclass
isinstance(obj,cls)检查是否obj是否是类 cls 的对象
isinstance(obj,cls)检查是否obj是否是类 cls 的对象
class A:pass class B(A):pass a = A() print(isinstance(a,A)) #t print(issubclass(B,A)) #t print(issubclass(A,B)) #f
反射
反射:是用 字符串类型的名字 去操作 变量。有 hasattr、getattr、 setattr、 delattr 四个相关函数。
与eval相似,但eval主要用来网络传输,操作文件或别人的代码,存在安全隐患。反射值操作写在内存中的名字,没有安全隐患。
反射对象中的属性和方法(getattr)
class A: def func(self): print('in func') a = A() a.name = 'alex' a.age = 63 # 反射对象的属性 ret = getattr(a,'name') # 通过变量名的字符串形式取到的值 print(ret) print(a.__dict__) 变量名 = input('>>>') # func print(getattr(a,变量名)) print(a.__dict__[变量名]) # 不能用于方法 # 反射对象的方法 a.func() ret = getattr(a,'func') ret()
反射类中的属性和方法(getattr/hasattr)
class A: price = 20 @classmethod def func(cls): print('in func') # 反射类的属性 # A.price print(getattr(A,'price')) # 反射类的方法 :classmethod staticmethod # A.func() if hasattr(A,'func'): getattr(A,'func')()
反射模块的属性和方法
import my 反射模块的属性 print(my.day) print(getattr(my,'day')) 反射模块的方法 getattr(my,'wahaha')()
# 内置模块也能用
#time
asctime
import time
print(getattr(time,'time')())
print(getattr(time,'asctime')())
反射自己模块的属性和方法
def qqxing(): print('qqxing') year = 2018 import sys print(sys.modules['__main__'].year) # 反射自己模块中的变量 # print(getattr(sys.modules['__main__'],'year')) # 反射自己模块中的函数 # getattr(sys.modules['__main__'],'qqxing')() 变量名 = input('>>>') print(getattr(sys.modules[__name__],变量名))
要反射的函数有参数怎么办?当有多个参数不同时,不可使用
print(time.strftime('%Y-%m-%d %H:%M:S'))
print(getattr(time,'strftime')('%Y-%m-%d %H:%M:S'))
一个模块中的类也能反射得到,只要是有 . 的都可以反射。
import my print(getattr(my,'C')()) # <my.C object at 0x0046BB70> if hasattr(my,'name'): getattr(my,'name')
setattr 、delattr
# 重要程度半颗星 # setattr 设置修改变量 class A: pass a = A() setattr(a,'name','nezha') setattr(A,'name','alex') print(A.name) print(a.name) # delattr 删除一个变量 delattr(a,'name') print(a.name) delattr(A,'name') print(a.name)
类的内置方法
内置的类方法 和 内置的函数 之间有着千丝万缕的联系。
__str__/__repr__
双下方法 obj.__str__ str(obj) obj.__repr__ repr(obj)
class Teacher: def __init__(self,name,salary): self.name = name self.salary = salary def __str__(self): return "Teacher's object :%s"%self.name def __repr__(self): return str(self.__dict__) def func(self): return 'wahaha' nezha = Teacher('哪吒',250) print(nezha) # 打印一个对象的时候,就是调用a.__str__ print(repr(nezha)) print('>>> %r'%nezha)
a.__str__ --> object object 里有一个__str__,一旦被调用,就返回调用这个方法的对象的内存地址 l = [1,2,3,4,5] # 实例化 实例化了一个列表类的对象 print(l) 走的是list类里面的__str__ %s str() 直接打印 实际上都是走的__str__ %r repr() 实际上都是走的__repr__ print(obj)/'%s'%obj/str(obj)的时候,(打印一个对象)
实际上是内部调用了obj.__str__方法,如果str方法有,那么他返回的必定是一个字符串。
如果没有__str__方法,会先找本类中的__repr__方法,再没有再找父类中的__str__。
(父类是自定义类时,如果没有__str__方法,会先到父类找str,再找自己的repr。再去找objiect类的str,repr)
总之,object在最后。
repr(),只会找__repr__,如果没有找父类的。repr 是str的备胎,但str不能做repr的备胎
内置的方法有很多 不一定全都在object中 class Classes: def __init__(self,name): self.name = name self.student = [] def __len__(self): return len(self.student) def __str__(self): return 'classes' py_s9= Classes('python全栈9期') py_s9.student.append('二哥') py_s9.student.append('泰哥') print(len(py_s9)) #走len print(py_s9)#走str
__del__
析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
class A: def __del__(self): # 析构函数: 在删除一个对象之前进行一些收尾工作 self.f.close() a = A() a.f = open() # 打开文件 第一 在操作系统中打开了一个文件 拿到了文件操作符存在了内存中 del a # a.f 拿到了文件操作符消失在了内存中 del a # del 既执行了这个方法,又删除了变量
__call__
对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class A: def __init__(self,name): self.name = name def __call__(self): ''' 打印这个对象中的所有属性 ''' for k in self.__dict__: print(k,self.__dict__[k]) a = A('alex')()
item系列 __getitem__\__setitem__\__delitem__
class A: def __init__(self,name,age): self.name = name self.age = age def __getitem__(self, item): if hasattr(self,item): return self.__dict__[item] def __setitem__(self, key, value): self.__dict__[key] = value def __delitem__(self, key): del self.__dict__[key] a = A('lily',23) print(a['name']) a['age']=18 print(a.age) print(a['age']) del a['age']# 通过自己实现的 del a.age # object 原生支持 __delattr__ print(a.__dict__)
(因为self的属性和值,通过字典实现,这个方法也可用于列表,用于下标取值)
__new__
# __init__ 初始化方法 # __new__ 构造方法 : 创建一个对象 class A: def __init__(self): self.x=1 print('in init') def __new__(cls, *args, **kwargs): print('in new') return object.__new__(A) a = A() # in new # in init
# 单例模式(一个类始终只有一个对象) class A: __obj = False def __init__(self,name,age): self.name = name self.age = age def __new__(cls, *args, **kwargs): if cls.__obj: return cls.__obj cls.__obj = object.__new__(A) return cls.__obj a1 = A('lisa',19) print(a1.__dict__) a2 = A('JISOO',20) a2.cloth = 'red-T' print(a2.__dict__) print(a1.__dict__) #与a2一样
__hash__
class A: def __init__(self,name,age,sex): self.name = name self.age = age def __hash__(self): return hash(self.name+self.sex) a = A('lisa',19,'women') b = A('jenny',23,'women') print(hash(a)) print(hash(b))
__eq__
未定义是比较的是内存地址,可以自定义比较的内容,灵活应用。 class A: def __init__(self,name): self.name = name def __eq__(self, other): if self.__dict__ == other.__dict__: return True else: return False a = A('lisa') b = A('lisa') print(a==b)
有一个类的init方法如下: class Person: def __init__(self,name,age,sex,weight): self.name = name self.sex = sex self.age = age self.weight = weight 假设有100个person的对象, 若两个对象的obj1,obj2的name和sex属性相同 即obj1.name==obj2.name and obj1.sex==obj2.sex 我们认为两个对象为同一个对象,已知一个列表中的100个对象,对这100个对象进行去重。 提示: 重写Person类重的两个内置方法 class A: def __init__(self,name,sex,age): self.name = name self.sex = sex self.age = age def __eq__(self, other): if self.name == other.name and self.sex == other.sex: return True return False def __hash__(self): return hash(self.name + self.sex) a = A('egg','男',38) b = A('egg','男',37) print(set((a,b))) # unhashable
import json from collections import namedtuple Card = namedtuple('Card',['rank','suit']) # rank 牌面的大小 suit牌面的花色 class FranchDeck: ranks = [str(n) for n in range(2,11)] + list('JQKA') # 2-A suits = ['红心','方板','梅花','黑桃'] def __init__(self): self._cards = [Card(rank,suit) for rank in FranchDeck.ranks for suit in FranchDeck.suits] def __len__(self): return len(self._cards) def __getitem__(self, item): return self._cards[item] def __setitem__(self, key, value): self._cards[key] = value def __str__(self): return json.dumps(self._cards,ensure_ascii=False) deck = FranchDeck() print(deck) from random import choice print(choice(deck)) #deck是对象,不是列表也可以吗 # print(choice(deck)) # 洗牌 from random import shuffle shuffle(deck) # print(deck) #把列表序列化后输出 # print(deck[:5]) #依赖于item方法