探討 Python 的物件生成 - tsungjung411/python-study GitHub Wiki

主題:

  • 動態生成 class
  • Singleton 實作方式(non thread safe)

動態生成 class:

class A: 
    def show(msg):
        print('A.msg: {}'.format(msg))

def showB(msg):
    print('B.msg: {}'.format(msg))

def __init__():
    print('__init__')

# dynamically define class B
class_name = 'B'
bases = (A,)
members = {
    '__init__': __init__,
    'showB': showB,
}
b = type(class_name, bases, members)

print('class B:', b)
print('base class of B:', b.__bases__)
print('b.mro():', b.mro())
b.show('hello')
b.showB('hello')

執行結果:

class B: <class '__main__.B'>
base class of B: (<class '__main__.A'>,)
b.mro(): [<class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
A.msg: hello
B.msg: hello

Singleton 實作方式(non thread safe) - 範例一:

class A:
    def __init__(self):
        print('A.__init__()')
    # end-of-def
        
    def show(self):
        print('A.show()')
    # end-of-def
    
# end-of-class

class B(A):
    __instance = None
    
    def __init__(self):
        print('B.__init__()')
    # end-of-def
        
    def __new__(cls, *args, **kwargs):
        print('B.__new__({}, {}, {})'.format(cls, args, kwargs))
        if not cls.__instance:
            cls.__instance = super().__new__(cls, *args, **kwargs)
            print('B.__new__(): first new')
        return cls.__instance
    # end-of-def
    
    def show(self):
        super().show()
    # end-of-def
# end-of-class

print('flow: __new__() -> __init__()')
print('-' * 45)
b1 = B()
b1.show()

print('-' * 45)
b2 = B.__call__() # equivalent to B()
b2.show()

print('-' * 45)
print('b1:', b1)
print('b2:', b2)
print('id(b1):', hex(id(b1)))
print('id(b2):', hex(id(b2)))

執行結果:

flow: __new__() -> __init__()
---------------------------------------------
B.__new__(<class '__main__.B'>, (), {})
B.__new__(): first new
B.__init__()
A.show()
---------------------------------------------
B.__new__(<class '__main__.B'>, (), {})
B.__init__()
A.show()
---------------------------------------------
b1: <__main__.B object at 0x7ffaf09d14e0>
b2: <__main__.B object at 0x7ffaf09d14e0>
id(b1): 0x7ffaf09d14e0
id(b2): 0x7ffaf09d14e0
  • __init__() 會呼叫兩次

Singleton 實作方式(non thread safe) - 範例二:

class A:
    def __init__(self):
        print('A.__init__()')
    # end-of-def
        
    def show(self):
        print('A.show()')
    # end-of-def
    
# end-of-class

# if not inherited from type, an error will happen:
# TypeError: object() takes no parameters
class SingletonB(type):
    __instance = None
    
    def __call__(cls, *args, **kwargs):
        print('>> B.__call__({}, {}, {})'.format(cls, args, kwargs))
        if not cls.__instance:
            cls.__instance = super().__call__(*args, **kwargs)
        # end-of-if
        print('<< B.__call__():', cls.__instance)
        return cls.__instance
    # end-of-def
# end-of-class

class B(A, metaclass=SingletonB):
    
    def __init__(self):
        print('B.__init__()')
    # end-of-def
    
    def __new__(cls, *args, **kwargs):
        print('B.__new__({}, {}, {})'.format(cls, args, kwargs))
        return super().__new__(cls, *args, **kwargs)
    # end-of-def
    
    def show(self):
        super().show()
    # end-of-def
# end-of-class

print('flow: __call__() -> __new__() -> __init__()')
print('-' * 45)
b1 = B()
b1.show()

print('-' * 45)
b2 = B.__call__() # equivalent to B()
b2.show()

print('-' * 45)
print('b1:', b1)
print('b2:', b2)
print('id(b1):', hex(id(b1)))
print('id(b2):', hex(id(b2)))

執行結果:

flow: __call__() -> __new__() -> __init__()
---------------------------------------------
>> B.__call__(<class '__main__.B'>, (), {})
B.__new__(<class '__main__.B'>, (), {})
B.__init__()
<< B.__call__(): <__main__.B object at 0x7ffaf091cba8>
A.show()
---------------------------------------------
>> B.__call__(<class '__main__.B'>, (), {})
<< B.__call__(): <__main__.B object at 0x7ffaf091cba8>
A.show()
---------------------------------------------
b1: <__main__.B object at 0x7ffaf091cba8>
b2: <__main__.B object at 0x7ffaf091cba8>
id(b1): 0x7ffaf091cba8
id(b2): 0x7ffaf091cba8
  • __init__() 只會呼叫一次

相關文章