第四十四篇 面向对象高阶

。_饼干妹妹 提交于 2020-03-13 05:36:48

第四十四篇 面向对象高阶

一、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
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!