初学python之路-day29

1.元类

#元类:
在 python中,一切皆对象,类也是对象,可以把一个类当成普通对象来使用,比如存储到列表中,或者作为参数传给函数等等...
由于对象是由类实例化产生的,而类对象是元类(type)实例化产生的
元类:用于产生类的类 一个类由三个部分组成
1.类的名称 2.类的父类们 3.类的名称空间 我们定义一个类时,采用type(类名,父类元组,名称空间字典) 方法,得到这个类 type(类名,父类元组,名称空间字典) #返回一个新的类 type(对象) #将会返回这个对象的类型 因此用class定义类时,解释器就会自动调用type方法来完成实例化
exec

exec用于执行字符串形式的python代码 只要符合python都能执行 ,并且可以指定将执行产生的名字放入某个名称空间 

class_text = """
class A:
    def test(self):
        print(self)
"""
loca2 = {}
exec(class_text,None,loca2)
print(loca2)

因此用exec方法是另一种得到类的方法


eval 用于执行简单的表达式,不能有任何的特殊语法,执行一行代码
自定义元类:
=======
##### 元类翻译为:metaclass  只要看见它就应该想起来这是元类 

我们在定义元类时  尽量在类名后添加MetaClass  方便阅读

应用:

当我们需要高度定制类时,如限制类名必须大写开头等等...

就需要使用元类,但是元类type中的代码 无法被修改 ,只能创建新的元类(继承自type) 通过覆盖`__init__`来完成对类的限制 

class MyMetaClass(type):
    pass

# 使用自定义元类
class Person(metaclass=MyMetaClass):
    pass
__init__方法

实例化对象时会自动执行类中的`__init__`方法, 类也是对象 ,在实例化类对象时会自动执元类中的`__init__`方法

并且传入类的三个必要参数,类的名字,父类们,名称空间

当然会自动传入类对象本身作为第一个参数  

##案例: 限制类名必须首字母大写   控制类中方法名必须全部小写
class MyMetaClass(type):
    def __init__(self,class_name,bases,name_dict):

        super().__init__(class_name,bases,name_dict)
        # 类名必须首字母大写  否则直接抛出异常
        if not class_name.istitle():
            print("类名必须大写 傻x!")
            raise Exception

        # 控制类中方法名必须全部小写
        for k in name_dict:
            if str(type(name_dict[k])) == "<class 'function'>":
                if not k.islower():
                    raise Exception
    pass

# 会自动调用其元类中的 __init__ 方法传入 类对象本身 类名称 父类们  名称空间
class Student(object,metaclass=MyMetaClass): # MyMetaClass("Student",(object,),{})
    NAME = 10
    def say(self):
        print("SAY")
    pass
__new__方法:

类中的new方法会在创建类对象时执行,并且先于init方法  

作用是创建一个类对象   

class A(metaclass=MyMetaClass):    

​    pass

1.执行MyMetaClass的__new__方法   拿到一个类对象 
2.执行MyMetaClass的__init__方法  传入类对象以及其他的属性 ,进行初始化

注意:1.如果覆盖了__new__一定也要调用type中的__new__并返回执行结果
2.在调用init方法前类对象已经创建完成了,所以如果对性能要求高的话 可以选在在new中完成定制   如果发现有问题,就不用创建类对象了 
__call__方法:

元类中的 call方法会在调用类对象实例化时执行,可以用于控制对象的创建过程


class MyMeta(type):

    # 获得某个类的实例
    def __call__(self, *args, **kwargs):
        print("call")
        # return super().__call__(*args,**kwargs)
        new_args = []
        for i in args:
            if isinstance(i,str):
                new_args.append(i.upper())
            else:
                new_args.append(i)
        return super().__call__(*new_args,**kwargs)



# 注意注意注意:  __new__  __init__ 是创建类对象时还会执行
# __call__ 类对象要产生实例时执行

class Student(metaclass=MyMeta):
    def __init__(self,name,gender,age):
        self.name = name
        self.gender = gender
        self.age = age

s = Student("jack","woman",18)
print(s.age)
print(s.gender)


class Person(metaclass=MyMeta):
    def __init__(self,name,gender):
        self.name = name
        self.gender = gender

p = Person("rose","man")
print(p.name)
总结:
**控制类的创建过程  使用__init__**
**控制对象的实例化过程 使用 __call__  也需要调用type的__call__ 并返回其结果**     
**__new__  会先于__init__执行 在__new__中必须 调用type的__new__  并返回结果 **
元类实现单例模式 

什么是单例:

​    某个类如果只有一个实例对象,那么该类成为单例类 

单例的好处:

​    当某个类的所有对象特征和行为完全一样时,避免重复创建对象,浪费资源

案例:
class SingletonMetaClass(type):
    #创建类时会执init 在这为每个类设置一个obj属性 默认为None
    def __init__(self,a,b,c):
        super().__init__(a,b,c)
        self.obj = None
    
    # 当类要创建对象时会执行 该方法
    def __call__(self, *args, **kwargs):
         # 判断这个类 如果已经有实例了就直接返回 从而实现单例
        if self.obj:
            return self.obj

        # 没有则创建新的实例并保存到类中
        obj = type.__call__(self,*args,**kwargs)
        self.obj = obj
        return obj

2.异常

异常:
异常是程序运行过程中发生的非正常情况,是一个错误发生时的信号

异常如果没有被正确处理的话,将导致程序被终止,这对于用户体验是非常差的,可能导致严重的后果  

处理异常的目的就是提高程序的健壮性 

异常的分类:
python解释器在执行代码前会先检查语法,语法检查通过才会开始执行代码

1.语法检测异常:作为一个合格的程序员 是不应该出现这种低级错误 

2.运行时异常:已经通过语法检测,开始执行代码,执行过程中发生异常 称之为运行时异常
异常的常见举例:
TypeError: 'int' object is not subscriptable     对象不能被切片  
TypeError: 'list' object is not callable        对象不能被调用
IndexError: list index out of range                索引超出范围
TypeError: 'builtin_function_or_method' object is not iterable     对象不能被迭代
KeyError: 'xxx'      不存在这个key
FileNotFoundError: [Errno 2] No such file or directory: 'xxxxx'  文件找不到

异常的组成:

Traceback (most recent call last):
  File "F:/day29/11.常见异常.py", line 22, in <module>
    with open("xxxxx") as f:
FileNotFoundError: [Errno 2] No such file or directory: 'xxxxx'

        
Traceback    是异常追踪信息   用于展示错误发生的具体位置 以及调用的过程
其中 包括了 错误发生的模块  文件路径   行号  函数名称  具体的代码

最后一行  前面是错误的类型  
         后面 错误的详细信息   在查找错误时 主要参考的就是详细信息
    

异常处理:

异常发生后,如果不正确就会导致程序终止,我们应尽量避免异常发生

语法:
try:

​    可能会出现异常的代码 放到try里面

except 具体异常类型 as e:

​    如果真的发生异常就执行except

处理异常:
1. 当发生异常  不是立马加try   要先找出错误原因并解决它  

2. try 仅在 即使你知道为什么发生错误 ,但是你却无法避免  
   例如   你明确告诉用户 需要一个正确文件路径 然而用户依然传入了错误的路径   

   如  socket    双方都要使用管道  ,但是如果一方有由于某些原因强行关闭了 ,即使你知道原因也无法避免出错   那就只能try 保证程序正常结束      

   总结一句话:能不加try 就不加try

   
## 自定义异常类:
当系统提供异常类不能准确描述错误原因时   就可以自定义异常类  
class  MyException(Exception):
    pass
继承自Exception即可 

主动抛出异常:
当我们做功能的提供者,给外界提供一个功能接口,但是使用者不按照相应的方式来使用,或者参数类型不正确等原因,导致功能无法正常执行时,就应该主动抛出异常

方法:使用raise  关键字 
关键字为任何Exception的子类   或是 对象
raise MyException
raise MyException("错误具体原因!")
断言assert:
断言  其实可以理解为断定的意思  

即非常肯定某个条件是成立的

条件是否成立其实可以使用if来判断

其存在的目的就是 为了简化if 判断而生的

猜你喜欢

转载自www.cnblogs.com/wangwei5979/p/10931201.html