• [Python之路] 元类(引申 单例模式)


    一、类也是对象

    当我们定义一个变量或者函数的时候,我们可以在globals()的返回值字典中找到响应的映射:

    def A():
        print("This is function A")
    
    
    myname = "Leo"
    
    
    print(globals())

    我们可以得到以下结果:

    {
        '__name__': '__main__',
        '__doc__': None,
        '__package__': None,
        '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000021F09971CC0>,
        '__spec__': None,
        '__annotations__': {},
        '__builtins__': <module 'builtins' (built-in)>,
        '__file__': 'D:/pycharm_project/leo1201/basic_class/test.py',
        '__cached__': None,
        'A': <function A at 0x0000021F099AC1E0>,
        'myname': 'Leo'
    }

    我们可以发现我们定义的函数A和变量myname都在这个字典中,这个字典中记录的映射,实际上就是全局变量(也就是可以直接调用的对象)。

    那么我们定义的类是什么呢:

    class B(object):
        pass
    
    
    print(globals())

    得到的结果:

    {
        '__name__': '__main__',
        '__doc__': None,
        '__package__': None,
        '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000021F09971CC0>,
        '__spec__': None,
        '__annotations__': {},
        '__builtins__': <module 'builtins' (built-in)>,
        '__file__': 'D:/pycharm_project/leo1201/basic_class/test.py',
        '__cached__': None,
        'B': <class '__main__.B'>
    }

    我们可以看到,类B也在该字典中。所以,我们说类实际上也是一个全局对象,只是这个对象的功能可以生成其他的对象而已。

    二、内建属性

    在上面所看到的globals()返回的字典中,我们并未看到我们常用的print()等直接调用的函数,这是因为这些内建函数都在'__builtin__'中:

    global_dict = globals()
    print(global_dict['__builtins__'])  # 打印 <module 'builtins' (built-in)>

    我们打印一下__builtin__字典:

    global_dict = globals()
    print(global_dict['__builtins__'].__dict__)

    得到一个很长的字典:

    {
        '__name__': 'builtins',
        '__doc__': "Built-in functions, exceptions, and other objects.
    
    Noteworthy: None is the `nil' object; Ellipsis represents `...' in slices.",
        '__package__': '',
        '__loader__': <class '_frozen_importlib.BuiltinImporter'>,
        '__spec__': ModuleSpec(name='builtins', loader=<class '_frozen_importlib.BuiltinImporter'>),
        '__build_class__': <built-in function __build_class__>,
        '__import__': <built-in function __import__>,
        'abs': <built-in function abs>,
        'all': <built-in function all>,
        'any': <built-in function any>,
        'ascii': <built-in function ascii>,
        'bin': <built-in function bin>,
        'breakpoint': <built-in function breakpoint>,
        'callable': <built-in function callable>,
        'chr': <built-in function chr>,
        'compile': <built-in function compile>,
        'delattr': <built-in function delattr>,
        'dir': <built-in function dir>,
        'divmod': <built-in function divmod>,
        'eval': <built-in function eval>,
        'exec': <built-in function exec>,
        'format': <built-in function format>,
        'getattr': <built-in function getattr>,
        'globals': <built-in function globals>,
        'hasattr': <built-in function hasattr>,
        'hash': <built-in function hash>,
        'hex': <built-in function hex>,
        'id': <built-in function id>,
        'input': <built-in function input>,
        'isinstance': <built-in function isinstance>,
        'issubclass': <built-in function issubclass>,
        'iter': <built-in function iter>,
        'len': <built-in function len>,
        'locals': <built-in function locals>,
        'max': <built-in function max>,
        'min': <built-in function min>,
        'next': <built-in function next>,
        'oct': <built-in function oct>,
        'ord': <built-in function ord>,
        'pow': <built-in function pow>,
        'print': <built-in function print>,
        'repr': <built-in function repr>,
        'round': <built-in function round>,
        'setattr': <built-in function setattr>,
        'sorted': <built-in function sorted>,
        'sum': <built-in function sum>,
        'vars': <built-in function vars>,
        'None': None, 'Ellipsis': Ellipsis,
        'NotImplemented': NotImplemented,
        'False': False, 'True': True,
        'bool': <class 'bool'>,
        'memoryview': <class 'memoryview'>,
        'bytearray': <class 'bytearray'>,
        'bytes': <class 'bytes'>,
        'classmethod': <class 'classmethod'>,
        'complex': <class 'complex'>,
        'dict': <class 'dict'>,
        'enumerate': <class 'enumerate'>,
        'filter': <class 'filter'>,
        'float': <class 'float'>,
        'frozenset': <class 'frozenset'>,
        'property': <class 'property'>,
        'int': <class 'int'>,
        'list': <class 'list'>,
        'map': <class 'map'>,
        'object': <class 'object'>,
        'range': <class 'range'>,
        'reversed': <class 'reversed'>,
        'set': <class 'set'>,
        'slice': <class 'slice'>,
        'staticmethod': <class 'staticmethod'>,
        'str': <class 'str'>,
        'super': <class 'super'>,
        'tuple': <class 'tuple'>,
        'type': <class 'type'>,
        'zip': <class 'zip'>,
        '__debug__': True,
        'BaseException': <class 'BaseException'>,
        'Exception': <class 'Exception'>,
        'TypeError': <class 'TypeError'>,
        'StopAsyncIteration': <class 'StopAsyncIteration'>,
        'StopIteration': <class 'StopIteration'>,
        'GeneratorExit': <class 'GeneratorExit'>,
        'SystemExit': <class 'SystemExit'>,
        'KeyboardInterrupt': <class 'KeyboardInterrupt'>,
        'ImportError': <class 'ImportError'>,
        'ModuleNotFoundError': <class 'ModuleNotFoundError'>,
        'OSError': <class 'OSError'>,
        'EnvironmentError': <class 'OSError'>,
        'IOError': <class 'OSError'>,
        'WindowsError': <class 'OSError'>,
        'EOFError': <class 'EOFError'>,
        'RuntimeError': <class 'RuntimeError'>,
        'RecursionError': <class 'RecursionError'>,
        'NotImplementedError': <class 'NotImplementedError'>,
        'NameError': <class 'NameError'>,
        'UnboundLocalError': <class 'UnboundLocalError'>,
        'AttributeError': <class 'AttributeError'>,
        'SyntaxError': <class 'SyntaxError'>,
        'IndentationError': <class 'IndentationError'>,
        'TabError': <class 'TabError'>,
        'LookupError': <class 'LookupError'>,
        'IndexError': <class 'IndexError'>,
        'KeyError': <class 'KeyError'>,
        'ValueError': <class 'ValueError'>,
        'UnicodeError': <class 'UnicodeError'>,
        'UnicodeEncodeError': <class 'UnicodeEncodeError'>,
        'UnicodeDecodeError': <class 'UnicodeDecodeError'>,
        'UnicodeTranslateError': <class 'UnicodeTranslateError'>,
        'AssertionError': <class 'AssertionError'>,
        'ArithmeticError': <class 'ArithmeticError'>,
        'FloatingPointError': <class 'FloatingPointError'>,
        'OverflowError': <class 'OverflowError'>,
        'ZeroDivisionError': <class 'ZeroDivisionError'>,
        'SystemError': <class 'SystemError'>,
        'ReferenceError': <class 'ReferenceError'>,
        'MemoryError': <class 'MemoryError'>,
        'BufferError': <class 'BufferError'>,
        'Warning': <class 'Warning'>,
        'UserWarning': <class 'UserWarning'>,
        'DeprecationWarning': <class 'DeprecationWarning'>,
        'PendingDeprecationWarning': <class 'PendingDeprecationWarning'>,
        'SyntaxWarning': <class 'SyntaxWarning'>,
        'RuntimeWarning': <class 'RuntimeWarning'>,
        'FutureWarning': <class 'FutureWarning'>,
        'ImportWarning': <class 'ImportWarning'>,
        'UnicodeWarning': <class 'UnicodeWarning'>,
        'BytesWarning': <class 'BytesWarning'>,
        'ResourceWarning': <class 'ResourceWarning'>,
        'ConnectionError': <class 'ConnectionError'>,
        'BlockingIOError': <class 'BlockingIOError'>,
        'BrokenPipeError': <class 'BrokenPipeError'>,
        'ChildProcessError': <class 'ChildProcessError'>,
        'ConnectionAbortedError': <class 'ConnectionAbortedError'>,
        'ConnectionRefusedError': <class 'ConnectionRefusedError'>,
        'ConnectionResetError': <class 'ConnectionResetError'>,
        'FileExistsError': <class 'FileExistsError'>,
        'FileNotFoundError': <class 'FileNotFoundError'>,
        'IsADirectoryError': <class 'IsADirectoryError'>,
        'NotADirectoryError': <class 'NotADirectoryError'>,
        'InterruptedError': <class 'InterruptedError'>,
        'PermissionError': <class 'PermissionError'>,
        'ProcessLookupError': <class 'ProcessLookupError'>,
        'TimeoutError': <class 'TimeoutError'>,
        'open': <built-in function open>,
        'quit': Use quit() or Ctrl-Z plus Return to exit,
        'exit': Use exit() or Ctrl-Z plus Return to exit,
        'copyright': Copyright (c) 2001-2018 Python Software Foundation.
        All Rights Reserved.
    }

    这个字典中包含着python的所有内建功能。我们可以从中找到print()、open()、exit()、False、Bool、dir()等我们常用的内建函数,以及很多异常类。

    我们通过使用该字典,可以直接队其中的内建函数进行调用:

    global_dict = globals()
    builtin_dict = global_dict['__builtins__'].__dict__
    builtin_dict['print']("Hello Builtin functions")  # 打印 Hello Builtin functions

    所以,我们平时在使用内建函数的时候,Python实际上就是在这个字典中查找我们调用的函数名是否存在,存在则调用,不存在则报错。

    三、元类

    我们知道,类可以创建实例对象。从前面我们可以看出,类实际上也是一种对象,那么类是谁的对象呢??答案是元类。

    当我们平时在创建类时:

    class Test1():
        num1 = 100
        num2 = 200
    
    
    help(Test1)

    通过help我们可以看到:

    class Test1(builtins.object)
     |  Data descriptors defined here:
     |  
     |  __dict__
     |      dictionary for instance variables (if defined)
     |  
     |  __weakref__
     |      list of weak references to the object (if defined)
     |  
     |  ----------------------------------------------------------------------
     |  Data and other attributes defined here:
     |  
     |  num1 = 100
     |  
     |  num2 = 200

    在Python中,元类就是type类,我们使用type来创建一个普通类:

    Test2 = type('Test2', (), {'num1': 100, 'num2': 200})
    help(Test2)

    通过help可以看到:

    class Test2(builtins.object)
     |  Data descriptors defined here:
     |  
     |  __dict__
     |      dictionary for instance variables (if defined)
     |  
     |  __weakref__
     |      list of weak references to the object (if defined)
     |  
     |  ----------------------------------------------------------------------
     |  Data and other attributes defined here:
     |  
     |  num1 = 100
     |  
     |  num2 = 200

    我们可以看到Test1和Test2基本是一样的。我们在程序中通过class关键字创建类,实际上和type()创建一个类,效果一样。

    在 Test2 = type('Test2', (), {'num1': 100, 'num2': 200}) 中,第一个参数是类名,第二个参数是父类,第三个参数是属性和方法(用字典列出)。

    这里注意:我们给一个类取名叫"Test2",那么type的返回值我们也应该用"Test2"来接收(当然也可以用其他的名字),如果不一致的话:

    Test222 = type('Test2', (), {'num1': 100, 'num2': 200})
    help(Test222)
    t = Test222()
    # t = Test2()  # 报错

    但在help中:

    class Test2(builtins.object)
     |  Data descriptors defined here:
     |  
     |  __dict__
     |      dictionary for instance variables (if defined)
     |  
     |  __weakref__
     |      list of weak references to the object (if defined)
     |  
     |  ----------------------------------------------------------------------
     |  Data and other attributes defined here:
     |  
     |  num1 = 100
     |  
     |  num2 = 200

    所以,我们尽量采用一致的名字, 避免出错。

    如果一个类继承于Test2:

    Test3 = type("Test3", (Test2,), {})

    类中的各种成员方法:

    def func1(self):
        print("This is method func1")
    
    
    @classmethod
    def func2(cls):
        print("This is class method func2")
    
    
    @staticmethod
    def func3():
        print("This is static method func3")
    
    
    Test2 = type('Test2', (), {"func1": func1, "func2": func2, "func3": func3})
    t = Test2()
    t.func1()
    Test2.func2()
    t.func3()

    四、元类什么时候用

    元类一般很少使用

    我们可以自定义元类,并用于创建普通类:

    第一种,用函数来修改属性名(在type运行之前):

    def upper_attr(class_name, class_parents, class_attr):
        new_attr = {}
        for name, value in class_attr.items():
            if not name.startswith("__"):
                new_attr[name.upper()] = value
    
        return type(class_name, class_parents, new_attr)
    
    
    class Foo(object, metaclass=upper_attr):
        bar = 'pig'
    
    
    print(hasattr(Foo, "bar"))  # 打印 Flase
    print(hasattr(Foo, "BAR"))  # 打印 True
    
    f = Foo()
    print(f.BAR)   # 打印 pig

    upper_attr会修改属性的名称为大写,然后再使用type生成一个Foo类。 

    第二种,定义一个继承于type的元类来创建Foo类:

    class UpperAttrMetaClass(type):
        def __new__(cls, class_name, class_parents, class_attr):
            print("Here is new")
            new_attr = {}
            for name, value in class_attr.items():
                if not name.startswith("__"):
                    new_attr[name.upper()] = value
    
            return type(class_name, class_parents, new_attr)
    
    
    class Foo(object, metaclass=UpperAttrMetaClass):
        def __init__(self, abv):
            self.abv = abv
            print("Here is init")
    
        bar = 'pig'
    
    
    print(hasattr(Foo, "bar"))  # 打印 Flase
    print(hasattr(Foo, "BAR"))  # 打印 True
    
    f = Foo()
    print(f.BAR)  # 打印 pig

    UpperAttrMetaClass继承于type,则他就变成了一个元类,由于元类在创建Foo类时会调用__new__函数,所以,我们在__new__函数中做了一些额外的操作。

    五、使用__new__实现单例模式(引申)

    既然我们可以在类创建对象之前在__new__中做事情,那么我们可以在__new__中判断这个类是否已经存在实例对象,如果存在则直接返回已存在的对象,如果不存在才创建新的对象,这就实现了单例模式。

    # -*- coding: utf-8 -*-
    import threading
    
    
    class Singleton(object):
        _lock = threading.Lock()
    
        def __new__(cls, *args, **kwargs):
            if not hasattr(Singleton, "_instance"):
                with Singleton._lock:    # 加锁防止多线程环境中两个线程同时判断到上一行代码为True,同时创建该类的实例
                    if not hasattr(Singleton, "_instance"):
                        # 调用object类的__new__方法
                        Singleton._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
            return Singleton._instance
    
    
    def test(number):
        s = Singleton()
        print str(number) + ": " + str(id(s))
    
    
    for i in range(10):
        t = threading.Thread(target=test, args=(i, ))
        t.start()
  • 相关阅读:
    15 react ajax 请求 github 用户信息
    14 react fetch
    13 React axios
    12 脚手架编写React项目(评论管理)---
    gitlab init project
    为什么是2MSL而不是MSL?
    mac python install zlib not available
    Laravel 传递数据到视图
    sleep(0)作用
    ping错误详解
  • 原文地址:https://www.cnblogs.com/leokale-zz/p/11985766.html
Copyright © 2020-2023  润新知