29 元类 异常

什么是元类

  一切皆对象 

  类也是对象,可以把一个类当成普通对象来使用,比如存储到列表中,或者作为参数传给函数等等...

  对象   通过类实例化产生的

  类对象 是由type实例化产生的

例子

class A :
pass
print(type(A))

调用Ttye 实例化产生一个类

一个类由三个部分产生
  类名字
  类的父类们

  名称空间

type(类名,父类元组,名称空间字典) #返回一个新的类

type(对象) #将会返回这个对象的类型

当你定义一个class时,解释器会自动调用type来完成类的实例化

动态语言
可以在运行期间 动态生成类
修改对象属性
静态语言
数据类型的检查是在运行前(如编译阶段)做的
方便调试,代码相对规范
eg:

模拟解释器创建类对象
def test1(a):
  print(a)

def test2(self,b):
  print(self,b)

class_name = "C"
bases = (object,)
name_dict = {"name":"jack","test1":test1,"test2":test2}

C = type(class_name,bases,name_dict)
print(C)
c1 = C()
print(c1)
c1.test2(100)

1.

exec 可以执行字符串形式的python代码 并且会把执行过程中产生的名字 放到局部名称空间中

glob = {}
local = {}

code = """
def test(a):
print(a)

"""

exec(code, glob, local)

# print(glob)
print(local)
local['test'](10)

2.

eval 用于执行简单的表达式,不能有任何的特殊语法
class_text = """
class A:
def test(self):
print(self)
"""

loca2 = {}
exec(class_text, None, loca2)
print(loca2)

# eval(class_text) 报错 用于简单的表达式

print(globals())
a = 100


def qq():
name = 11

元类

用于产生类的类 

定义元类时,尽量在类名后添加

MetaClass   方便阅读

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

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

def SAYHL(self):
print(self.name)

type类已经具备了创建类的能力

,但是现在不能满足我们的需求
需要对已有的功能进行扩展或修改
方式
1.直接修改源代码 行不通
2.自己定义新的元类

class MyMetaClass(type):
pass


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

 
__init__方法

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

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

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



def __init__方法(类对象本身 类名 父类们 名称空间):
  pass

自动调用其元类中的__init__方法传入
eg:
class MyMetaClass(type):
pass

# 默认创建类时 是找的type来实例化的
class People(metaclass=MyMetaClass):
pass
class Student:
def __init__(self):
pass

print(type(People))
print(type(Student))

s = Student()
# 实例化对象时 ,1.产生空对象 2.自动执行__init_
People = MyMetaClass()
# 创建类对象时也是一样的 会先创建空的类对象 再调用__init__()方法
# 创建类时
class MyMetaClass(type):

# 类的实例化
def __init__(self, class_name, bases, name_dict):
"""
元类的应用
self:表示的是类对象
:param class_name:类名
:param bases:父类们
:param name_dict:名称空间
"""

# 调用父类的初始化
super().__init__(class_name, bases, name_dict)
print(name_dict)
# 类名必须首字母大写 否则抛出异常
if not class_name.istitle():
print('类名必须大写,')
raise Exception

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


# 正确写入
class Students(object, metaclass=MyMetaClass):
NAME = 10

def say(self):
print('SAY')
pass


# 当 类名 或 方法 大写或小写时 会抛出异常

__new__方法

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

作用是创建一个类对象

class A(metaclass=MyMetaClass):

​ pass

1.执行MyMetaClass的`__new__`方法 拿到一个类对象

2.执行MyMetaClass的`__init__` 方法 传入类对象以及其他的属性 ,进行初始化

注意:如果覆盖了`__new__` 一定也要调用type中的`__new__`并返回执行结果

##### 使用new方法也可以完成定制类的工作 和init有什么区别?

在调用init方法前类对象已经创建完成了

所以如果对性能要求高的话 可以选在new中完成定制 如果发现有问题,就不用创建类对象了

class MyClass(type):

def __init__(self, class_name, bases, name_dict):
# 调用父类的初始化
# super().__init__(class_name, bases , name_dict)
print('init')
pass
# __new__()方法
# def __new__ 创建类对象
# 该方法会在实例化类对象时自动调用并且在 init 之前调用
# 其作用时用于创建新的类对象的
# 注意这里必须调用type类中的__new__ 否则将无法产生类对象 并且返回其结果


def __new__(cls, *args,**kwargs):
"""
cls 表示元类自己 即MyMetaClass
return 结果
"""
return type.__new__(cls,*args,**kwargs)
# 如果覆盖__new__ 一定要写上这行代码 并返回

class Peopel(metaclass=MyClass):
pass

print(Peopel)

# 就算__init__中什么都不写 这个类对象其实已经创建完成了 该有的属性都有了
# 这是与普通类不同之处

print(Peopel.__name__)

练习
# 需求: 要求每个类必须包含__doc__属性 自定义元类 覆盖
__doc__ 用于访问一个对象的注释信息

class A:
"""
qwertyu
asdfgjhk

"""
pass
print(A.__doc__)
# 你要控制类的创建 那就自定义元类 覆盖__init__

class DocMeatClass(type):
def __init__(self,class_name,bases,name_dict):
super().__init__(class_name,bases,name_dict)
if not self.__doc__:
raise Exception
print(type(object))

`__call__`方法(重点)

元类中的 call方法会在调用类时执行,

可以用于控制对象的创建过程,

class MyMate(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__方法
























猜你喜欢

转载自www.cnblogs.com/komorebi/p/10920853.html