我有一个类和一个普通的构造函数,但我希望预处理参数并对结果进行后处理,所以我提供了一个强制性的工厂构造函数。是的,我知道这对工厂来说是一个不寻常的含义,我也知道我可以使用记忆来完成我的处理,但我在扩展记忆类时遇到了问题。
我希望防止自己意外使用普通构造函数,这是一种方法。
import inspect
class Foo():
def __init__(self):
actual_class_method = Foo.Factory
# [surely there's a way to do this without introspection?]
allowed_called_from = {name:method for name,method in inspect.getmembers(Foo, inspect.ismethod)}['Factory']
actual_called_from = inspect.currentframe().f_back.f_code # .co_name)
print("actual class method = ",actual_class_method," id = ",id(actual_class_method),",name = ",actual_class_method.__name__)
print("allowed called from = ",allowed_called_from,", id = ",id(allowed_called_from),", name =",allowed_called_from.__name__)
print()
print("actual called from = ",actual_called_from,", id = ",id(actual_called_from),", name =",actual_called_from.co_name)
@classmethod
def Factory(cls):
Foo()
Foo.Factory()
产生输出
actual class method = <bound method Foo.Factory of <class '__main__.Foo'>> id = 3071817836 ,name = Factory
allowed called from = <bound method Foo.Factory of <class '__main__.Foo'>> , id = 3072138412 , name = Factory
actual called from = <code object Factory at 0xb7118f70, file "/home/david/Projects/Shapes/rebuild-v0/foo.py", line 15> , id = 3071381360 , name = Factory
假设我希望检查 Foo(( 的构造函数是否已从其工厂调用。我可以找到有关调用 Foo(( 的方法的各种内容,例如它的名称和编译它的文件名,这足以阻止我意外地直接调用它,但我看不到一种说法(调用 Foo(( 的方法(是(类 Foo 中的方法 Factory((。有没有办法做到这一点?
Alex Martelli发布了一个答案。
这可能会让你得到你想要的:
class Foo:
def __init__(self):
print('Foo.__init__') # might consider throwing an exception
@classmethod
def makeit(cls):
self = cls.__new__(cls)
self.foo = 'foo'
return self
f = Foo() # accidentally create Foo in the usual way
g = Foo.makeit() # use the 'One True Way' of making a Foo
print(g.foo)
print(f.foo)
输出:
Foo.__init__
foo
Traceback (most recent call last):
File "D:pythonsoMetaClassWorkAroundInit.py", line 19, in <module>
print(f.foo)
AttributeError: 'Foo' object has no attribute 'foo'
没有inspect
,你可以为构造函数提供一个默认参数,并检查传递的值是否是你期望的值(默认值为 0(
只有工厂有机会传递正确的初始化值(或者有人真的想调用构造函数,但不是偶然的(
class Foo():
__MAGIC_INIT = 12345678
def __init__(self,magic=0):
if magic != self.__MAGIC_INIT:
raise Exception("Cannot call constructor, use the factory")
@classmethod
def Factory(cls):
return Foo(magic=Foo.__MAGIC_INIT)
f = Foo.Factory() # works
f = Foo() # exception
另一种变化是切换一个私有的"锁定"布尔值。如果设置为 True
,则在进入构造函数时崩溃,否则让它完成工作,然后重置为 True
。
当然,因子可以访问此布尔值,并且可以在调用构造函数之前将其设置为 False
:
class Foo():
__FORBID_CONSTRUCTION = True
def __init__(self):
if self.__FORBID_CONSTRUCTION:
raise Exception("Cannot call constructor, use the factory")
Foo.__FORBID_CONSTRUCTION = True # reset to True
@classmethod
def Factory(cls):
Foo.__FORBID_CONSTRUCTION = False
return Foo()
f = Foo.Factory()
print("OK")
f = Foo()
即使多线程也不是问题,这要归功于python GIL。