第四十四篇 面向对象高阶
一、isinstance()和issubclass()
1.isinstance()
判断对象是否为这个类实例化出来的,也会检测父类(比较数据类型推荐使用)
class Foo:
pass
class Bar(Foo):
pass
obj = Bar()
# isinstance 可以检测到父类
print(isinstance(obj,Foo)) # True
print(isinstance(obj,Bar)) # True
print(isinstance(Bar,Foo)) # False
# type 只检测到类
print(type(obj) == Bar) # True
print(type(obj) == Foo) # False
print(type(obj)) # <class '__main__.Bar'>
print(type(Bar())) # <class '__main__.Bar'>
print(type(Foo())) # <class '__main__.Foo'>
# type 获取实例化对象的类,不会检测父类,可以用于生成类(type是元类)
2.issubclass()
比较判断某个类是否为另一个类的子类
class Foo:
pass
class Bar(Foo):
pass
print(issubclass(Bar, Foo))
二、反射
1.import()
通过字符串导入模块
time = __import__('time')
print(time.time())
2.hasattr()
hasattr:通过字符串判断是否类属性存在
class Foo:
def eat(self):
print('rice')
f = Foo()
print(hasattr(f,'eat')) # True
3.getattr()
getattr:通过字符串获取类属性
class Foo:
def eat(self):
print('rice')
f = Foo()
print(getattr(f,'eat')) # 得到一个方法对象和地址
print(getattr(f,'eat')())
'''
rice
None # 返回值为空
'''
4.setattr()
setattr:通过字符串修改类属性。如果有就修改,没有就添加
class Foo:
def eat(self):
print('rice')
f = Foo()
setattr(f,'eat','fruit')
print(f.__dict__) # {'eat': 'fruit'}
setattr(f,'play','game') # {'eat': 'fruit', 'play': 'game'}
5.delattr()
delattr:通过字符串删除类属性
class Foo:
def eat(self):
print('rice')
f = Foo()
print(f.__dict__) # {'eat':'rice'}
delattr(f,'eat')
print(f.__dict__) # {}
三、call() 和 new()
1.call()
cls()()就会触发__call__()
class Foo:
def __init__(self):
print('Foo()会触发,也就是实例化对象时触发')
def __call__(self):
print('Foo()()会触发,也即是对象调用方法时触发(其实主要用于元类中)')
obj = Foo() # Foo()会触发...
obj() # Foo()()会触发...
2.new()
1.用于实例化一个空对象时使用
2.new(cls,*more),它的上面是一个非绑定方法装饰器@staticmethod
class Foo:
def __new__(self): # self:类本身
print('__new__')
obj = object.__new__(self) # 实例化一个空对象。self:类本身
'''obj = self.__new__(self) 这样会出错,可以将self换成其他有__new__()方法的类,object作为基类就有__new__()方法'''
return obj
def __init__(self): # self 对象本身
print('__init__')
f = Foo()
四、元类
1.元类用来造类的
2.元类(),会触发_init_()生成类(元类实例化)
3.元类()(),会触发_call_()生成对象(类实例化)
4.类分为几部分:class_name类名/class_dict类体名称空间/class_bases父类们
5.对象的属性查找顺序:对象---->类---->父类(如果有其他父类,也会广度优先查找其他父类)---->祖宗类---->object类---->自定义元类---->type
# 第一种方法造类:使用type模仿class关键字造类
# 步骤:
# 1.类名class_name:比如 Foo
# 2.类体代码class_body:开辟内存空间,把属性/方法放入一个名称空间(造好了才会有名称空间:如 Foo.__dict__),用class_dict来接
# 3.父类(基类)class_bases:(object,)
# 4.exec() 方法:会把字符里的代码运行,并且放入名称空间
class_name = 'Foo'
class_bases = (object,)
class_body = '''
def __init__(self,name):
self.name = name
def speak(self):
print('666')
'''
class_dict = dict()
exec(class_body,{},class_dict) # 将类体代码中的名字放入名称空间
cls_foo = type(class_name,class_bases,class_dict) # 元类实例化,生成类
obj = cls_foo('king') # 类实例化,生成对象
# 第二种方法造类:使用元类造类(可以控制造类的过程)
class MyMeta(type): # 同样需要type元类来帮自定义的元类实例化一个类
def __init__(self,class_name,class_bases,class_dict):
# 利用type元类来实例化一个类
# 可以在这个位置加上逻辑代码,控制类的产生
if not class_name.title():
raise TypeError('类名首字母必须大写')
super().__init__(class_name,class_bases,class_dict)
class Foo(object,metaclass=MyMeta): # metaclass=MyMeta是关键
def __init__(self):
pass
obj = Foo() # 实例化一个对象
# 控制对象的产生
class MyMeta(type):
def __init__(self,class_name,class_bases,class_dict):
# 这个位置控制类的产生
super().__init__(class_name,class_bases,class_dict)
def __call__(self,*args,**kwargs):
# 控制实例化对象时参数的传递
obj = self.__new__(self)
self.__init__(obj,*args,**kwargs)
# 控制对象的产生
return obj
class Foo(object,metaclass=MyMeta):
def __init__(self):
pass
obj = Foo()
五、单例模式
1.单例模式(Singleton Pattern):是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。
比如,某个服务器程序的配置信息存放在一个文件中,客户端通过一个 AppConfig 的类来读取配置文件的信息。如果在程序运行期间,有很多地方都需要使用配置文件的内容,也就是说,很多地方都需要创建 AppConfig 对象的实例,这就导致系统中存在多个 AppConfig 的实例对象,而这样会严重浪费内存资源,尤其是在配置文件内容很多的情况下。事实上,类似 AppConfig 这样的类,我们希望在程序运行期间只存在一个实例对象。
2.实现方法有许多种,我们来介绍下面这三种
- 1.利用类绑定方法
NAME = 'king'
class Foo:
__instance = None # 定义一个隐藏的类属性,用于接住实例化对象并返回
def __init__(self, name):
self.name = name
@classmethod # 类绑定方法
def func(cls):
if cls.__instance: # 第一次不走这里
return cls.__instance # 第一次之后返回的都是相同的对象
obj = cls(NAME) # 这里写死了,只能接收NAME,所以只能实例化同一个对象
cls.__instance = obj # 类的__instance属性同样写死了,只能是这个对象了
return cls.__instance
f = Foo.func() # <__main__.Foo object at 0x000002079E3FC7B8>
f1 = Foo('jojo') # <__main__.Foo object at 0x000002079E3FC7B8>
- 2.利用装饰器
NAME = 'king'
# 传入类的装饰器
def deco(cls):
cls.__instance = cls(NAME) # 实例化一个固定的对象
def wrapper(*args,**kwargs):
if len(args) == 0 and len(kwargs) == 0:
return cls.__instance # 当不传参时,就是固定的对象
return cls(*args,**kwargs) # 传参之后就返回定制的对象
return wrapper
@deco
calss Foo:
def __init__(self, name):
self.name = name
obj = Foo()
- 3.利用元类(重要)
NAME = 'king'
# 元类
class MyMeta(type):
def __init__(self,class_name,class_bases,class_dict):
super().__init__(self,class_name,class_bases,class_dict) # 实例化一个类
self.__instance = self(NAME) # 实例化一个对象
def __call__(self,*args,**kwargs):
if len(args) == 0 and len(kwargs) == 0:
return self.__instance
obj = self.__new__(self) # 实例化一个空对象
self.__init__(obj,*args,**kwargs) # 初始化对象
return obj
class Foo(object,metaclass=MyMeta): # 会先去MyMeta中实例化一个类
def __init__(self,name):
self.name = name
obj = Foo() # 实例化一个对象
六、异常处理
# 1.捕捉异常
try:
# 疑似有问题的代码,可以通过这个方法查看是否有报错
except:
print('error')
# 2.Exception 万能捕捉
try:
# 疑似有问题的代码,可以通过这个方法查看是否有报错
except Exception as e:
print(e)
# 3.finally 无论是否报错,都会执行后面的代码,一般用于文件的关闭(不推荐)
try:
# 疑似有问题的代码,可以通过这个方法查看是否有报错
except Exception as e:
print(e)
finally:
print('keep')
# 4.raise() 主动抛出异常
try:
# 代码块
raise NameError('not defind')
except NameError as e:
print(e)
# 5.assert 断言,用于预估代码会出错,给个标记,方便报错时查找(不推荐)
x = 0
y = x**2
assert y!=2