面向对象进阶
一、isinstance 和 issubclass
1、对象与类之间的关系:判断第一个参数是否是第二个参数的实例
isinstance(obj,cls)检查obj是否是类 cls 的对象
# 2 == 3 # 值是否相等 # 2 is 3 # 内存地址是否相等 class A:pass class B(A):pass class C(B):pass c = C() print(isinstance(c,A)) # 包含继承关系的判断 print(type(c) is B) print(type(c) is A) # type只关心创建这个对象的类
2、类与类之间的关系
第一个参数是疑似子类,第二个参数是疑似父类.
最终结果如果真的是父子类关系,则返回True
class A:pass class B(A):pass print(issubclass(A,B))-----F print(issubclass(B,A))-----T
二、反射
定义:通过字符串来操作Python代码中的函数、变量、方法和类
1、getattr 获取属性
2、hasattr 判断有没有该属性
class A: name = 'alex' # 静态 属性 age = 83 # 静态 属性 # hasattr getattr print(getattr(A,'name')) print(hasattr(A,'name')) >>> alex True
在类中使用 类中获取类的静态属性
tag = input('>>>') if hasattr(A,tag): print(getattr(A,tag))
属性的值 = getattr(类名,字符串数据类型的属性名)
如果第二个参数是不存在的属性名,则会报错
class Student: def __init__(self,name,age): self.name = name self.age = age def show(self): for key in self.__dict__: print(key,self.__dict__[key]) yuan = Student('苑昊',38) yuan.show() >> name 苑昊 age 38 if hasattr(yuan,'name'): print(getattr(yuan,'name')) >>>苑昊 if hasattr(yuan,'show'): func = getattr(yuan,'show') func() >>> name 苑昊 age 38
hasassr & getattr
类.静态属性:
getattr(类,'静态属性')
对象.方法:
getattr(对象,'方法')() 直接反射得到一个方法之后调用
对象.对象属性
应用场景 : 网络编程 从文件中读取信息反映到编程中
setattr(设置、修改属性)接受3个参数
delattr (删除属性)
class Student: def __init__(self,name,age): self.name = name self.age = age def show(self): for key in self.__dict__: print(key,self.__dict__[key]) hei = Student('小黑',18) # hei.sex = '不详' # print(hei.sex) setattr(hei,'sex','不详') # 增改操作 print(hei.sex) setattr(hei,'sex','male') print(hei.sex) print(hei.__dict__) delattr(hei,'sex') # 删除操作 print(hei.__dict__) >>> 不详 male {'name': '小黑', 'age': 18, 'sex': 'male'} {'name': '小黑', 'age': 18}
class Student: def __init__(self,name,age): self.name = name self.age = age def show(self): for key in self.__dict__: print(key,self.__dict__[key]) hei = Student('小黑',18) def wahaha(a,b): # 专属于某一个对象的静态方法 print(a,b) setattr(hei,'func',wahaha) #这样写有问题 print(hei.__dict__) hei.func(1,2) >>> {'name': '小黑', 'age': 18, 'func': <function wahaha at 0x0000000001D01E18>} 1 2
总结:
hasattr 判断某一个 变量 是否能够.调用一个名字,返回True或者False
getattr 直接获取一个变量中的名字的值
setattr 为一个变量增加或者修改一个属性
delattr 删除一个变量中的属性或者方法
反射类中的名字 getattr(类名,'静态属性') getattr(类名,'类方法')() getattr(类名,'静态方法')() 反射对象中的名字 getattr(对象名,'对象属性') getattr(对象名,'方法名')() 反射模块中的名字 import 模块名 getattr(模块名,'模块中的变量') getattr(模块名,'模块中的函数')() getattr(模块名,'模块中的类名') 反射当前模块中的名字 import sys getattr(sys.modules[__name__],'变量') getattr(sys.modules[__name__],'函数')() getattr(sys.modules[__name__],'类名')
import my_moudle print(my_moudle.money) print(getattr(my_moudle,'money')) getattr(my_moudle,'qqxing')()
def sww():
print('爽歪歪')
count = 0
import sys
print(sys.modules[__name__]) # 一定是aaa.py
print(sys.modules['__main__']) # 不确定的
print(getattr(sys.modules[__name__],'count'))
getattr(sys.modules[__name__],'sww')()
print(sys.modules[__name__])
# print(sys.modules['my_moudle'])
# print(my_moudle)
print(getattr(sys.modules['my_moudle'],'qqxing'))
sys.modules[__name__]-----> 引用当前模块的内容
class Student: def __init__(self,name,age): self.name = name self.age = age def show_student(self): for key in self.__dict__: print(key,self.__dict__[key]) class Teacher: def __init__(self, name, age): self.name = name self.age = age def show_teacher(self): for key in self.__dict__: print(key, self.__dict__[key]) class Manager: def __init__(self, name, age): self.name = name self.age = age def show_manager(self): for key in self.__dict__: print(key, self.__dict__[key]) import sys main = sys.modules[__name__] cls_name = input('>>>') # 根据输入反射找到具体的类 if hasattr(main,cls_name): cls = getattr(main,cls_name) # 实例化对象 alex = cls('alex',81) print(type(alex)) # 展示这个对象中的所有方法 for i in alex.__dict__: print(i,alex.__dict__[i]) >>> <class '__main__.Teacher'> name alex age 81
三、面向对象中的内置方法
__方法名__
内置方法:直接和Python内部隐形相关的
len()内置函数 __len__()
hash()哈希函数 __hash__()
str() __str__()
class A: def __init__(self,name,cls,age,sex): self.name = name self.cls = cls self.age = age self.sex = sex def __eq__(self, other): # if self.__dict__ == other.__dict__:return True return True def __len__(self): return len(self.__dict__) hei = A('小黑','py10期',18,'无') hei2 = A('小2黑','py11期',17,'无') def len(obj): return obj.__len__() print(len(hei)) # 4 print(hei.__dict__) # {'name': '小黑', 'cls': 'py10期', 'age': 18, 'sex': '无'} print(hei2.__dict__) # {'name': '小2黑', 'cls': 'py11期', 'age': 17, 'sex': '无'} print(hei == hei2) # Ture
# _*_coding:utf-8_*_ format_dict={ 'nat':'{obj.name}-{obj.addr}-{obj.type}',#学校名-学校地址-学校类型 'tna':'{obj.type}:{obj.name}:{obj.addr}',#学校类型:学校名:学校地址 'tan':'{obj.type}/{obj.addr}/{obj.name}',#学校类型/学校地址/学校名 } class School: def __init__(self,name,addr,type): self.name=name self.addr=addr self.type=type def __format__(self, format_spec): return format_spec.format(obj=self) s1=School('oldboy1','北京','私立') print(format(s1,format_dict['tna'])) print(format(s1,format_dict['nat'])) print(format(s1,format_dict['tan'])) print(format(s1,'tna')) print(format(s1,'tan')) >>> oldboy1-北京-私立 私立/北京/oldboy1 tna tan
class School: def __init__(self,name,addr,type): self.name=name self.addr=addr self.type=type def __str__(self): return str(self.__dict__) def __repr__(self): # repr是str的备胎 return 'repr : school的repr对象' s1=School('oldboy1','北京','私立') print(str(s1)) print('%s'%s1) print(s1) print(repr(s1)) print('%r'%s1) >>> {'name': 'oldboy1', 'addr': '北京', 'type': '私立'} {'name': 'oldboy1', 'addr': '北京', 'type': '私立'} {'name': 'oldboy1', 'addr': '北京', 'type': '私立'} repr : school的repr对象 repr : school的repr对象
1、__del__ 析构方法:删除一个对象的时候调用的方法
import time class A: def __del__(self): # 析构函数 print('删除一个对象的时候调用我') a = A() time.sleep(1)
class A: def __init__(self): self.f = open('userinfo','a') def consume(self): pass def __del__(self): '''在删除一个对象之前做一些收尾工作''' self.f.close() print('删除一个对象的时候调用我') a = A() time.sleep(1)
del a 删除一个对象
删除一个对象的时候,如果内部存在__del__方法,
那么在删除一个对象之前先执行__del__方法中的代码
2、__new__ 构造方法
new一个对象
obj.__new__( )
class A: def __init__(self): print('执行init方法了') def __new__(cls): print('执行new方法了') return object.__new__(cls) # 创造对象,将对象返回 a = A() print(type(a)) print(type(A)) >>> 执行new方法了 执行init方法了 <class '__main__.A'> <class 'type'>
先执行__new__方法 创造出一个对象
然后把创造出来的对象传递给__init__方法
会把self自动的返回,被a接收
元类:
有一个元类 在创建类 # type() 所有直接用class创建出来的类的元类都是type # # class 类名(B,classMeta = 元类名) # class 类名(B,classMeta = type) # 默认 # # 元类 创造 类 所以所有的类的type都是它的元类,默认是type # 类 创造 对象 具体创造对象的方法 __new__方法,所有的对象的type都是它对应的类 python中 一切皆对象 变量 都有它属于的数据类型
3、设计模式---单例设计(关联__new__)
一个类 可以被多次实例化 但是同一时间,在python的内存中,只能有一个实例
class A: _instance = None def __init__(self,name): '''给娃穿衣服''' self.name = name def __new__(cls, *args, **kwargs): '''生娃的过程''' if not A._instance: A._instance = object.__new__(cls) return A._instance a1 = A('alex') # 第一次实例化的时候创造一个实例 print(a1.name) a2 = A('egon') print(a1,a2) # 'alex' 'alex' >>> alex egon egon
也可以使用hasattr()方法
class A: def __init__(self,name): '''给娃穿衣服''' self.name = name def __new__(cls, *args, **kwargs): '''生娃的过程''' if not hasattr(A,'_instance'): A._instance = object.__new__(cls) return A._instance a1 = A('alex') # 第一次实例化的时候创造一个实例 print(a1.name) a2 = A('egon') print(a1.name,a2.name) # 'alex' 'alex'
4、item系列 - ---- 与字典dict样式关联
a.属性>>>>a [ 属性 ]
class A: def __init__(self,name): self.name = name self.age = 81 def __getitem__(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('alex') print(a['name']) # 对应了类中一个方法的语法 相当于 a.name print(a['age']) # 对应了类中一个方法的语法 相当于 a.age # 增加 和 修改一个属性 a['sex'] = '不详' # a.sex = '不详' print(a.__dict__) print(a.sex) print(a['sex']) a['sex'] = '女' print(a.__dict__) # 删除 del a['sex'] print(a.__dict__) # del a # print(a) 报错 >>>> alex 81 {'name': 'alex', 'age': 81, 'sex': '不详'} 不详 不详 {'name': 'alex', 'age': 81, 'sex': '女'} {'name': 'alex', 'age': 81}
5、__call__方法 ------ 对应 类名( ) ( )
(装X方法)
class A: def __call__(self,a): print('执行我了',a) def call(self,a): print('执行我',a) a = A() a('aaa') # __call__ 对应 对象() a.call('aaa') >>> 执行我了 aaa 执行我 aaa
6、hash 方法
class A: def __hash__(self): return 123 a = A() b = A() print(hash(a)) # object.__hash__() print(hash(a)) # object.__hash__() >>> 123 123
# hash(obj)函数,obj对象对应的类必然内部实现了__hash__方法
# hash的结果就是__hash__方法的返回值
# 且在一次成的执行过程中是不会发生变化的
# 且要想作为字典的key或者作为集合的元素,这个对象对应的类必须实现__hash__方法
补充:
一、面试试题
# 金融公司面试题 # 有一个类,对应这个类产生了100个对象 # 每个对象有三个属性 : 姓名 年龄 性别 # 请对这一百个对象进行去重,如果姓名和性别相同,即便年龄不同也是相同的对象 # 问最简便的方法? class Person: def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex def __hash__(self): return hash('%s%s'%(self.name,self.sex)) def __eq__(self, other): if self.name == other.name and \ self.sex == other.sex: return True p_lst = [] for i in range(100): p_lst.append(Person('egon',i,'male')) print(p_lst) print(set(p_lst)) # 报错不可hash 完成了__hash__ # 去重需要两个条件 # hash是否相等 __hash__ # 值是否相等 __eq__
收获1
对于一些python当中已经存在的内置函数 内置数据类型 内置模块中的方法
都有可能依赖于类中的内置方法
收获2
set方法依赖集合中元素对象的__hash__ __eq__
二、纸牌游戏
class FranchDeck: ranks = [str(n) for n in range(2,11)] + list('JQKA') 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] deck = FranchDeck() print(deck[0]) from random import choice print(choice(deck)) print(choice(deck))
class FranchDeck: ranks = [str(n) for n in range(2,11)] + list('JQKA') 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 deck = FranchDeck() print(deck[0]) from random import choice print(choice(deck)) print(choice(deck)) from random import shuffle shuffle(deck) print(deck[:5])