Python中的私有构造函数
如何创建一个只能由类的静态函数调用而不能从其他地方调用的私有构造函数?首先,术语“构造函数”不适用于Python,因为尽管Python中的私有构造函数,python,static,constructor,private,Python,Static,Constructor,Private,如何创建一个只能由类的静态函数调用而不能从其他地方调用的私有构造函数?首先,术语“构造函数”不适用于Python,因为尽管\uuuu init\uuu()方法扮演一个角色,它只是一个方法,当对象已经创建并且需要初始化时调用它 Python中类的每个方法都是公共的。程序员通常在方法名称中用\uu或\u标记“私有”方法,例如: # inheriting from object is relevant for Python 2.x only class MyClass(object): #
\uuuu init\uuu()
方法扮演一个角色,它只是一个方法,当对象已经创建并且需要初始化时调用它
Python中类的每个方法都是公共的。程序员通常在方法名称中用\uu
或\u
标记“私有”方法,例如:
# inheriting from object is relevant for Python 2.x only
class MyClass(object):
# kinda "constructor"
def __init__(self):
pass
# here is a "private" method
def _some_method(self):
pass
# ... and a public one
def another_method(self):
pass
引述:
此外,下列特殊形式使用前导或尾随
下划线可以识别(通常可以与任何情况结合使用)
公约):
:弱“内部使用”指示器。例如,M中的“\u单个\u前导\u下划线
“不导入名称以下划线开头的对象” 导入*
单尾随下划线:按惯例使用,以避免与 Python关键字,例如。
Tkinter.Toplevel(master,class='ClassName')
:命名类属性时,调用name 损坏(在类FooBar中,\uuuuuuuuuuuuuuuuuuu前导下划线
变为\uuboo
;见下文)\ufoobar\uboo
:“魔法”对象或 位于用户控制的名称空间中的属性。例如,\uuuuu双\u前导\u和尾随\u下划线\uuuuuuu
,\uuuuu init\uuuu
或\uuuu导入\uuuu
。永远不要捏造这样的名字;只使用它们 如文件所述\uuuu文件\uuuu
\uuuuu init\uuuu
,它修改刚刚创建的对象(实际上它已经将self
作为第一个参数)
无论如何,python基于这样一个假设:每个人都是同意的成年人,因此private/public并不像其他语言那样被强制执行
正如其他一些响应者所提到的,应该是“私有”的方法通常用一个或两个下划线作为前缀:\u private
或\u private
。两者之间的区别在于后者会扰乱方法的名称,因此您将无法从对象实例化外部调用它,而前者不会
例如,如果类A
同时定义了\u private(self)
和\u private(self)
:
您通常希望使用单下划线,因为-特别是在单元测试中-使用双下划线会使事情变得非常棘手
HTH!因为还没有人提到过这一点——您可以在很大程度上控制哪些名称在哪些作用域中可见,并且有很多作用域可用。下面是另外两种将类的构造限制为工厂方法的方法:
#Define the class within the factory method
def factory():
class Foo:
pass
return Foo()
或
(注意:这仍然允许从外部引用类(例如,对于isinstance
检查),但很明显,您不应该直接实例化它。)
或
这使得访问Foo
类变得不可能(至少在不使用至少一个magic(双下划线)属性的情况下),但仍然允许多个函数使用它(通过在删除全局Foo名称之前定义它们)
class MongoConn(object):
@classmethod
def instance(cls):
if not hasattr(cls, '_instance'):
cls._instance = cls()
return cls._instance
def __init__(self):
assert not hasattr(self.__class__, '_instance'), 'Do not call constructor directly!'
如果您想要一个实例。前缀
\uu
和\u
不能提供将对象实例化限制为特定“工厂”的解决方案,但是Python是一个强大的工具箱,可以通过多种方式实现所需的行为(正如@Jesse W at Z所演示的)。
下面是一个可能的解决方案,它可以使类公开可见(允许isinstance
等),但可以确保仅通过类方法进行构造:
class OnlyCreatable(object):
__create_key = object()
@classmethod
def create(cls, value):
return OnlyCreatable(cls.__create_key, value)
def __init__(self, create_key, value):
assert(create_key == OnlyCreatable.__create_key), \
"OnlyCreatable objects must be created using OnlyCreatable.create"
self.value = value
使用create
类方法构造对象:
>>> OnlyCreatable.create("I'm a test")
<__main__.OnlyCreatable object at 0x1023a6f60>
如果试图通过模仿create
类方法创建对象
由于编译器损坏了OnlyCreatable,创建失败。\uu createKey
>>> OnlyCreatable(OnlyCreatable.__createKey, "I'm a test")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: type object 'OnlyCreatable' has no attribute '__createKey'
>>>onlycreateable(onlycreateable.\uu createKey,“我是一个测试”)
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
AttributeError:类型对象“OnlyCreatable”没有属性“\uu createKey”
在类方法之外构造
OnlyCreatable
的唯一方法是知道OnlyCreatable.\uuuu create\u key
的值。由于该类属性的值是在运行时生成的,并且其名称的前缀是.\uuuuuu。将其标记为不可访问,因此实际上无法获取该值和/或构造对象 您可以通过抽象类实现这种效果。任何需要在“私有构造函数”中定义的实例属性都可以是抽象属性。然后,您的工厂类方法通过填充这些抽象属性以及执行任何其他初始化工作(如数据验证)来构建自己的具体类
from abc import ABC, abstractmethod
class Foo(ABC):
@property
@abstractmethod
def _a(self) -> int:
pass
def bar(self) -> int:
return self._a + 1
@classmethod
def from_values(cls, a: int) -> 'Foo':
class _Foo(cls):
def __init__(self, __a):
self.__a = __a
@property
def _a(self):
return self.__a
return _Foo(a)
Foo() # TypeError: Can't instantiate abstract class ...
Foo.from_values(1).bar() # 1
如果您发现在Foo
上不需要抽象属性,那么在调用Foo()
时将不会得到TypeError
。在这种情况下,您可以依赖ABC
的继承作为文档,或者为安全起见定义一个虚拟属性
可能的调整
- 需要可变实例属性吗?添加setter
- 不关心类和实例属性之间的差异吗?使用
class _Foo(cls): _a = a return _Foo()
>>> OnlyCreatable(0, "I'm a test")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 11, in __init__
AssertionError: OnlyCreatable objects can only be created using OnlyCreatable.create
>>> OnlyCreatable(OnlyCreatable.__createKey, "I'm a test")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: type object 'OnlyCreatable' has no attribute '__createKey'
from abc import ABC, abstractmethod
class Foo(ABC):
@property
@abstractmethod
def _a(self) -> int:
pass
def bar(self) -> int:
return self._a + 1
@classmethod
def from_values(cls, a: int) -> 'Foo':
class _Foo(cls):
def __init__(self, __a):
self.__a = __a
@property
def _a(self):
return self.__a
return _Foo(a)
Foo() # TypeError: Can't instantiate abstract class ...
Foo.from_values(1).bar() # 1
class _Foo(cls):
_a = a
return _Foo()
from math import cos, sin
class Point(metaclass=NoPublicConstructor):
def __init__(self, x, y):
self.x = x
self.y = y
@classmethod
def from_cartesian(cls, x, y):
return cls._create(x, y)
@classmethod
def from_polar(cls, rho, phi):
return cls._create(rho * cos(phi), rho * sin(phi))
Point(1, 2) # raises a type error
Point.from_cartesian(1, 2) # OK
Point.from_polar(1, 2) # OK