元类与单例解析

一、eval、exec内置函数

1、eval函数

eval内置函数的使用场景:
①执行字符串会得到相应的执行结果
②一般用于类型转换得到dict、list、tuple等

2、exec函数

exec应用场景 用来辅助了解元类的概念

①执行字符串没有执行结果(没有返回值)

②将执行的字符串中产生的名字形成对应的局部名称空间

解析exec函数
exec函数帮我们运行字符串中的代码,把字符串中产生的名字放到名称空间里

自己定义全局作用域中的名字和字典,自己定义局部作用域的名字和字典
将所要执行的代码放到字符串中,在字符串中写好操作全局作用域的和局部作用域的

字符串内放一堆代码,exec来运行字符串中的代码的时候产生的名字都会放到名称空间里,
放到自己定义的全局或者是局部的名称空间里,如果声明是global,就会放到global的字典里
exec(字符串,global_dic, local_dic)
code = """
global x
x=0
y=2
"""
global_dic = {'x': 100000}
local_dic = {}
exec(code, global_dic, local_dic)
print(global_dic)
print(local_dic)   # {'y': 2}


code
= """ x=1 y=2 def f1(self,a,b): pass """ local_dic = {} exec(code, {}, local_dic) print(local_dic) # {'x': 1, 'y': 2, 'f1': <function f1 at 0x00000000027A8C80>}

 

二、类的名称空间的控制

class A:
    pass

print(A.__dict__)

a = A()
print(a, type(a))   #  <__main__.A object at 0x00000000021E7400> <class '__main__.A'>

print(A, type(A))   # <class '__main__.A'> <class 'type'>

print(type)        # <class 'type'>
print(type(type))   # <class 'type'>


python中万物皆对象,所有用来创建对象的类,本身也是对象,类是type类的对象
type类叫做元类,是所有元类的基类
元类:制造类的类 --类的类
控制类的产生
控制类的对象的产生


s = '''
my_a = 10
my_b = 20
def __init__(self):
    pass
@classmethod
def print_msg(cls, msg):
    print(msg)
'''
dic = {}
exec(s, {}, dic)

C = type('C', (object, ), dic)
print(C, type(C), C.__bases__)   # <class '__main__.C'> <class 'type'> (<class 'object'>,)
print(C.__dict__)
c1 = C()
print(c1.my_a)     # 10
C.print_msg('12345')


class D:
    my_a = 10
    my_b = 20

    def __init__(self):
        pass
    @classmethod
    def print_msg(cls, msg):
        print(msg)

c1 = C()
print(c1.my_a)     # 10
C.print_msg('12345')   # 12345

 

三、元类

1、定义:

python中万物皆对象,所有用来创建对象的类,本身也是对象,类是type类的对象

type类叫做元类,是所有元类的基类

元类:制造类的类 --类的类  

      控制类的产生 

      控制类的对象的产生

2、__init__、__call__、__new__的用法

①__init__:实例化的时候会自动触发__init__的执行

②__call__:调用类的时候会自动触发

class Zoo:
    def __init__(self):
        print('1')       # 对象实例化时候触发
    def __str__(self):   # 打印对象的时候产生
        return 'abc'
    def __del__(self):   # 删除对象的时候触发
        pass
    def __call__(self, *args, **kwargs):   # 调用类的时候触发
        print('2')
obj = Zoo()   # 1
print(obj)    # abc
obj()         # 2     # 调用对象

③__new__:

class CountError(Exception):
    pass


class MyMeta(type):

    # 自定义元类,重写init方法的目的:
    # 1.该方法是从type中继承来的,所以参数同type的init
    # 2.最终的工作(如果开辟空间,如果操作内存)还是要借助type
    # 3.在交给type最终完成工作之前,可以对类的创建加以限制 *****
    def __init__(cls, class_name: str, bases, namespace):
        print(cls)   # <class '__main__.Student'>
        print(class_name)  # Student
        print(bases)   # (<class 'object'>,)
        print(namespace)   # {'__module__': '__main__', '__qualname__': 'Student', '__init__': <function Student.__init__ at 0x0000000001EA89D8>}
        # 需求:由给元类控制的类的类名必须首字母大写
        if not class_name.istitle():
            raise NameError('名字首字母必须大写')
        # 需求:定义类是必须明确父类
        if len(bases) == 0:
            raise CountError('父类必须明确')

        # super(MyMeta, cls).__init__(class_name, bases, namespace)
        super().__init__(class_name, bases, namespace)


    # 自定义元类,重写call方法的目的:
    # 1.被该元类控制的类生成对象,会调用元类的call方法
    # 2.在call中的返回值就是创建的对象
    # 3.在call中
    #       通过object开辟空间产生对象
    #       用被控制的类回调到自己的init方法完成名称空间的赋值
    #       将修饰好的对象反馈给外界
    def __call__(cls, *args, **kwargs):
        print('call fn run')               # call fn run
        obj = object.__new__(cls)
        # 需求:所有通过该元类控制的类产生的对象都有meta_name该属性
        obj.meta_name = cls.__name__
        # obj.name = args[0]
        # 调回当前被控制的类自身的init方法,完成名称空间的赋值
        cls.__init__(obj, *args, **kwargs)
        return obj



# Student = MyMeta(class_name, bases, namespace)
class Student(object, metaclass=MyMeta):
    def __init__(self, name, age):
        print('init fn run')       # init fn run
        self.name = name
        self.age = age
        pass

# stu = Student()

# class Teacher(Student, metaclass=MyMeta):
#     pass


stu = Student('Bob', 18)
print(stu, stu.name, stu.age)   # <__main__.Student object at 0x0000000001EB7FD0> Bob 18
stu = Student('Tom', 20)
print(stu.meta_name)            # Student

3、自定义元类控制类的创建

需求:类名的首字母必须大写;类中必须写好文档注释

①需求:类名首字母必须大写
class Mymeta(type):
    def __init__(self, class_name, class_bases, class_dic):
        if not class_name.istitle():
            raise TypeError('类名必须大写')
        super().__init__(class_name, class_bases, class_dic)   # 重用父类的方法

class Foo(object, metaclass=Mymeta):
    pass


②需求: 类中必须有文档注释
class Mymeta(type): def __init__(self, class_name, class_bases, class_dic): if not class_dic.get('__doc__'): raise TypeError('类中必须写好文档注释') super().__init__(class_name, class_bases, class_dic) class Zoo(metaclass=Mymeta): """ 文档注释 """ pass

 

四、单例

1、定义:一个类只能产生一个实例

2、为什么要有单例:

①该类需要对象的产生

②对象一旦产生,在任何位置再实例化对象,只能得到第一次实例化出来的对象

3、单例实现的四种方法:

①方法1
class Songs():
    __instance = None
    @classmethod
    def getInstance(cls):
        # 对象没有创建返回,有直接返回
        if cls.__instance == None:
            cls.__instance = cls()
        return cls.__instance

# 约定别用 类名() 来实例化对象,用类方法来获取唯一对象
s1 = Songs()
s2 = Songs()

print(s1)         # <__main__.Songs object at 0x0000000001E776D8>
print(s2)         # <__main__.Songs object at 0x0000000001E77710>
print(s1 is s2)   # False

s1 = Songs.getInstance()
s2 = Songs.getInstance()

print(s1)          # <__main__.Songs object at 0x00000000027A76D8>
print(s2)          # <__main__.Songs object at 0x00000000027A76D8>
print(s1 is s2)    # True


②方法2
class Songs:
    __instance = None
    def __new__(cls, song_name, *args, **kwargs):
        if cls.__instance == None:
            cls.__instance = object.__new__(cls)
            cls.__instance.song_name = song_name
        return cls.__instance

    def change_song(self, song_name):
        self.song_name = song_name

s1 = Songs('菊花爆满山')
s2 = Songs('感觉身体被掏空')
print(s1.song_name, s2.song_name)  # 菊花爆满山  菊花爆满山
s2.change_song('感觉身体被掏空')
print(s1.song_name, s2.song_name)  # 感觉身体被掏空 感觉身体被掏空


③方法3:采用装饰器
def outer(cls):
    _instance = None
    def inner(*args, **kwargs):
        nonlocal _instance
        if _instance == None:
            _instance = cls(*args, **kwargs)
        return _instance
    return inner

@outer  # Songs = outer(Songs)
class Songs:
    pass

s1 = Songs()
s2 = Songs()
print(s1)   # <__main__.Songs object at 0x0000000001EB7748>
print(s2)   # <__main__.Songs object at 0x0000000001EB7748>
print(s1 is s2)   # True


④方法4
class SingleMeta(type):
    __instance = None
    def __call__(cls, *args, **kwargs):
        if SingleMeta.__instance == None:
            SingleMeta.__instance = object.__new__(cls)
            cls.__init__(SingleMeta.__instance, *args, **kwargs)
        return SingleMeta.__instance

class Songs(metaclass=SingleMeta):
    def __init__(self):
        pass
    pass

s1 = Songs()
s2 = Songs()
print(s1)         # <__main__.Songs object at 0x0000000001E775C0>
print(s2)         # <__main__.Songs object at 0x0000000001E775C0>
print(s1 is s2)   # True

 

posted @ 2019-04-27 20:41  TianShu  Views(145)  Comments(0Edit  收藏  举报