Python 中的私有构造函数

发布于 2024-12-17 08:46:27 字数 48 浏览 0 评论 0原文

如何创建一个私有构造函数,该构造函数只能由类的静态函数调用,而不是从其他地方调用?

How do I create a private constructor which should be called only by the static function of the class and not from else where?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(15

倥絔 2024-12-24 08:46:28
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!'

如果您想要单个实例。

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!'

If you want a single instance.

荒人说梦 2024-12-24 08:46:27

___ 前缀不提供将对象实例化限制到特定“工厂”的解决方案,但是 Python 是一个强大的工具箱,所需的行为可以是通过不止一种方式实现(正如 Z 的 @Jesse W 所证明的那样)。
这是一个可能的解决方案,可以使类保持公开可见(允许 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(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

如果尝试通过模仿 create 类方法来创建对象
由于 OnlyCreatable.__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'

在类方法之外构造 OnlyCreatable 的唯一方法是了解 OnlyCreatable.__create_key 的值。由于该类属性的值是在运行时生成的,并且其名称以 __ 为前缀,将其标记为不可访问,因此实际上“不可能”获取该值和/或构造对象。

The _ and __ prefixes don't offer a solution to restricting instantiation of an object to a specific 'factory', however Python is a powerful toolbox and the desired behaviour can be achieved in more than one way (as @Jesse W at Z has demonstrated).
Here is a possible solution that keeps the class publicly visible (allowing isinstance etc.) but ensures construction is only possible by class-methods:

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

Constructing an object with the create class-method:

>>> OnlyCreatable.create("I'm a test") 
<__main__.OnlyCreatable object at 0x1023a6f60>

When attempting to construct an object without using the create class-method creation fails due to the assertion:

>>> 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

If attempting to create an object by mimicking the create class-method
creation fails due to compiler mangling of OnlyCreatable.__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'

The only way to construct OnlyCreatable outside of a class-method is to know the value of OnlyCreatable.__create_key. Since this class-attribute's value is generated at runtime and it's name is prefixed with __ marking it as inaccessible it is effectively 'impossible' to obtain this value and/or construct the object.

染柒℉ 2024-12-24 08:46:27

如何创建私有构造函数?

从本质上讲,这是不可能,因为 python 并不像您来自其他 OOP 语言时所想象的那样使用构造函数,而且因为 python 不强制隐私,它只是有一个特定的语法来建议给定的方法/属性应该被视为私有的。让我详细说明一下...

首先:您可以在 python 中找到的最接近构造函数的是 __new__ 方法 但这很少使用(通常使用 __init__ ,它修改刚刚创建的对象(实际上它已经有 self作为第一个参数)。

无论如何,Python 是基于每个人都是一个假设的。 同意成人,因此私有/公共并不像其他一些

响应者提到的那样强制执行,意味着“私有”的方法通常前面加上一个或两个下划线:<。 code>_private 或 __private 两者的区别在于后者会打乱方法的名称,因此您将无法从对象实例化外部调用它,而前者没有。

例如,如果您的类 A 定义了 _private(self)__private(self)

>>> a = A()
>>> a._private()   # will work
>>> a.__private()  # will raise an exception

您通常希望使用单个下划线,如下所示- 特别是对于单元测试 - 有双下划线会让事情变得非常棘手......

HTH!

How do I create a private constructor?

In essence, it's impossible both because python does not use constructors the way you may think it does if you come from other OOP languages and because python does not enforce privacy, it just has a specific syntax to suggest that a given method/property should be considered as private. Let me elaborate...

First: the closest to a constructor that you can find in python is the __new__ method but this is very very seldom used (you normally use __init__, which modify the just created object (in fact it already has self as first parameter).

Regardless, python is based on the assumption everybody is a consenting adult, thus private/public is not enforced as some other language do.

As mentioned by some other responder, methods that are meant to be "private" are normally prepended by either one or two underscores: _private or __private. The difference between the two is that the latter will scramble the name of the method, so you will be unable to call it from outside the object instantiation, while the former doesn't.

So for example if your class A defines both _private(self) and __private(self):

>>> a = A()
>>> a._private()   # will work
>>> a.__private()  # will raise an exception

You normally want to use the single underscore, as - especially for unit testing - having double underscores can make things very tricky....

HTH!

花海 2024-12-24 08:46:27

尽管 Python 中不存在严格的私有属性,但您可以使用元类来防止使用 MyClass() 语法来创建 MyClass 对象。

以下是改编自 Trio 项目的示例:

from typing import Type, Any, TypeVar


T = TypeVar("T")


class NoPublicConstructor(type):
    """Metaclass that ensures a private constructor

    If a class uses this metaclass like this:

        class SomeClass(metaclass=NoPublicConstructor):
            pass

    If you try to instantiate your class (`SomeClass()`),
    a `TypeError` will be thrown.
    """

    def __call__(cls, *args, **kwargs):
        raise TypeError(
            f"{cls.__module__}.{cls.__qualname__} has no public constructor"
        )

    def _create(cls: Type[T], *args: Any, **kwargs: Any) -> T:
        return super().__call__(*args, **kwargs)  # type: ignore

这是一个使用示例:

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

Though strictly private attributes do not exist in Python, you can use a metaclass to prevent the use of the MyClass() syntax to create a MyClass object.

Here is an example adapted from the Trio project:

from typing import Type, Any, TypeVar


T = TypeVar("T")


class NoPublicConstructor(type):
    """Metaclass that ensures a private constructor

    If a class uses this metaclass like this:

        class SomeClass(metaclass=NoPublicConstructor):
            pass

    If you try to instantiate your class (`SomeClass()`),
    a `TypeError` will be thrown.
    """

    def __call__(cls, *args, **kwargs):
        raise TypeError(
            f"{cls.__module__}.{cls.__qualname__} has no public constructor"
        )

    def _create(cls: Type[T], *args: Any, **kwargs: Any) -> T:
        return super().__call__(*args, **kwargs)  # type: ignore

Here is an example of use:

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
弥繁 2024-12-24 08:46:27

您可以对哪些名称在哪些范围内可见有相当大的控制权——并且有很多可用的范围。这里有两种三种其他方法来将类的构造限制为工厂方法:

#Define the class within the factory method
def factory():
  class Foo:
    pass
  return Foo()

OR

#Assign the class as an attribute of the factory method
def factory():
  return factory.Foo()
class Foo:
  pass
factory.Foo = Foo
del Foo

(注意:这仍然允许从外部引用该类(对于isinstance 检查,例如),但很明显您不应该直接实例化它。)

或者

#Assign the class to a local variable of an outer function
class Foo:
  pass
def factory_maker():
  inner_Foo=Foo
  def factory():
    return inner_Foo()
  return factory
factory = factory_maker()
del Foo
del factory_maker

这使得它不可能(至少,不使用至少一个神奇的(双下划线)属性)来访问Foo 类,但仍然允许多个函数使用它(通过在删除全局 Foo 名称之前定义它们)。

You can have considerable control over what names are visible in what scopes -- and there are lots of scopes available. Here are two three other ways to limit construction of a class to a factory method:

#Define the class within the factory method
def factory():
  class Foo:
    pass
  return Foo()

OR

#Assign the class as an attribute of the factory method
def factory():
  return factory.Foo()
class Foo:
  pass
factory.Foo = Foo
del Foo

(Note: This still allows the class to be referred to from outside (for isinstance checks, for example), but it makes it pretty obvious that you aren't supposed to instantiate it directly.)

OR

#Assign the class to a local variable of an outer function
class Foo:
  pass
def factory_maker():
  inner_Foo=Foo
  def factory():
    return inner_Foo()
  return factory
factory = factory_maker()
del Foo
del factory_maker

This makes it impossible (at least, without using at least one magic (double underscore) property) to access the Foo class, but still allows multiple functions to use it (by defining them before deleting the global Foo name.

凤舞天涯 2024-12-24 08:46:27

如果使用模块级函数而不是静态方法就可以...

将整个类设为私有,将 API 作为抽象类公开,并使用函数来实例化您的私有类

class Foo(ABC):
    @abstractmethod
    def bar(self) -> int:
        ...


class _SpecificFoo(Foo):
    def __init__(self, bar: int):
        self._bar = bar

    def bar(self) -> int:
        return self._bar


def specific_foo(bar: int) -> Foo:
    return _SpecificFoo(bar)

注意

  • _SpecificFoo()specific_foo() 可以有不同的签名
  • specific_foo() 返回一个 Foo。 API 没有提及 _SpecificFoo

这与 Jesse 的第一个选项非常相似,但每次调用 specific_foo 时不会重新定义该类,并且类接口是静态类型的。

If a module-level function is OK instead of a static method ...

Make the whole class private, expose the API as an abstract class, and use a function to instantiate your private class

class Foo(ABC):
    @abstractmethod
    def bar(self) -> int:
        ...


class _SpecificFoo(Foo):
    def __init__(self, bar: int):
        self._bar = bar

    def bar(self) -> int:
        return self._bar


def specific_foo(bar: int) -> Foo:
    return _SpecificFoo(bar)

Note

  • _SpecificFoo() and specific_foo() can have different signatures
  • specific_foo() returns a Foo. The API makes no mention of _SpecificFoo

This is very similar to Jesse's first option, but the class isn't redefined on each call to specific_foo, and the class interface is statically typed.

提笔落墨 2024-12-24 08:46:27

引用 Python 风格指南 (PEP 8)

此外,以下使用前导或尾随的特殊形式
下划线被识别(这些通常可以与任何情况组合
约定):

  • _single_leading_underscore:弱“内部使用”指示器。例如“来自 M
    import *
    " 不会导入名称以下划线开头的对象。

  • single_trailing_underscore_:按惯例使用以避免与
    Python 关键字,例如
    Tkinter.Toplevel(master, class_='ClassName')

  • __double_leading_underscore:命名类属性时,调用 name
    损坏(在 FooBar 类中,__boo 变为 _FooBar__boo;见下文)。

  • __double_leading_and_trailing_underscore__:“神奇”对象或
    存在于用户控制的命名空间中的属性。例如__init__
    __import____file__。永远不要发明这样的名字;只使用它们
    如文档所述。

Quoting the Python style guide (PEP 8):

In addition, the following special forms using leading or trailing
underscores are recognized (these can generally be combined with any case
convention):

  • _single_leading_underscore: weak "internal use" indicator. E.g. "from M
    import *
    " does not import objects whose name starts with an underscore.

  • single_trailing_underscore_: used by convention to avoid conflicts with
    Python keyword, e.g.
    Tkinter.Toplevel(master, class_='ClassName')

  • __double_leading_underscore: when naming a class attribute, invokes name
    mangling (inside class FooBar, __boo becomes _FooBar__boo; see below).

  • __double_leading_and_trailing_underscore__: "magic" objects or
    attributes that live in user-controlled namespaces. E.g. __init__,
    __import__ or __file__. Never invent such names; only use them
    as documented.

陌生 2024-12-24 08:46:27

首先,“构造函数”这个术语不适用于Python,因为尽管__init__()方法起到了一个作用,但它只是一个在创建对象时调用的方法。并且需要初始化。

Python 中类的每个方法都是公共的。通常程序员在方法名称中使用 ___ 标记“私有”方法,例如:

# 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

Fist of all, the term "constructor" does not apply to Python, because, although __init__() method plays a role of one, it is just a method which is called when an object has already been created and requires initialization.

Every method of a class in Python is public. Generally programmers mark "private" methods with _ or __ in the name of a method, e.g.:

# 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
成熟的代价 2024-12-24 08:46:27

其他答案提到Python中不存在“私有”构造函数(或一般的访问修饰符)之类的东西。虽然这是事实,但您仍然可以利用 __ 前缀行为,通过使用双下划线前缀“隐藏”嵌套类中的实际实现来实现相同的最终效果:

class MyPublicClassWithPrivateConstructor:

    class __Implementation:
        def __init__(self, *args, **kwargs):
            # do the things
            self.args = args
            self.kwargs = kwargs

    @classmethod
    def make_my_thing(cls, *args, **kwargs)
        return cls.__Implementation(*args, **kwargs)

    def __new__(cls, *args, **kwargs):
        raise Exception("MyPublicClassWithPrivateConstructor cannot be initialized directly, use MyPublicClassWithPrivateConstructor.make_my_thing")

# elsewhere
from whateva import MyPublicClassWithPrivateConstructor

MyPublicClassWithPrivateConstructor.__Implementation  # type object 'MyPublicClassWithPrivateConstructor' has no attribute '__Implementation'
MyPublicClassWithPrivateConstructor.make_my_thing  # <bound method MyPublicClassWithPrivateConstructor.make_my_thing of <class 'whateva. MyPublicClassWithPrivateConstructor'>>
           

Other answers have mentioned that there is no such thing as "private" constructors (or access modifiers in general) in Python. While this is true, you can still take advantage of the __ prefixing behavior to accomplish the same end effect by "hiding" the actual implementation in a nested class with a double-underscore prefix:

class MyPublicClassWithPrivateConstructor:

    class __Implementation:
        def __init__(self, *args, **kwargs):
            # do the things
            self.args = args
            self.kwargs = kwargs

    @classmethod
    def make_my_thing(cls, *args, **kwargs)
        return cls.__Implementation(*args, **kwargs)

    def __new__(cls, *args, **kwargs):
        raise Exception("MyPublicClassWithPrivateConstructor cannot be initialized directly, use MyPublicClassWithPrivateConstructor.make_my_thing")

# elsewhere
from whateva import MyPublicClassWithPrivateConstructor

MyPublicClassWithPrivateConstructor.__Implementation  # type object 'MyPublicClassWithPrivateConstructor' has no attribute '__Implementation'
MyPublicClassWithPrivateConstructor.make_my_thing  # <bound method MyPublicClassWithPrivateConstructor.make_my_thing of <class 'whateva. MyPublicClassWithPrivateConstructor'>>
           
空城缀染半城烟沙 2024-12-24 08:46:27

如果模块级函数而不是静态方法可以,请参阅我的其他答案

您可以通过以下方式实现此效果一个抽象类。任何需要在“私有构造函数”中定义的实例属性都可以是抽象属性。然后,您的工厂类方法通过填充这些抽象属性以及执行任何其他初始化工作(例如数据验证)来构建自己的具体类。

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 的继承作为文档,或者定义一个虚拟属性以确保安全。

可选调整

  • 需要可变实例属性?添加设置器。
  • 不关心类属性和实例属性之间的区别吗?简化为
    类 _Foo(cls):
        _a=a
    
    返回 _Foo()
    

If a module-level function is OK instead of a static method, see my other answer

You can achieve something to this effect with an abstract class. Any instance attributes that need defining in the "private constructor" can be abstract properties. Your factory class method then builds its own concrete class by populating these abstract attributes, as well as doing any other initialisation work such as data validation.

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

If you find you need no abstract attributes on Foo, you won't get the TypeError when calling Foo(). In that case you can either rely on the inheritance from ABC as documentation, or define a dummy attribute for safety.

Optional tweaks

  • Need mutable instance attributes? Add setters.
  • Don't care about the difference between class and instance attributes? Simplify with
    class _Foo(cls):
        _a = a
    
    return _Foo()
    
横笛休吹塞上声 2024-12-24 08:46:27

如果您只是想要一种简单的方法来表明不应直接使用构造函数,则可以遵循以下模式以使构造函数的默认用法引发异常:

class Foo:
    def __init__(self, arg, __private=False):
        assert __private, "Constructor shouldn't be called directly"
        self.arg = arg

    @classmethod
    def from_bar(cls, bar: Bar):
        return cls(bar.arg, __private=True)

    @classmethod
    def from_baz(cls, baz: Baz):
         return cls(baz.arg, __private=True)

当然,任何人仍然可以执行 Foo ('abc', __private=True) 但它会发出一个明确的信号,表明不支持这种行为,这遵循 Python 的一般设计。

If you just want a simple way to signal that a constructor isn't supposed to be used directly, you can follow the following pattern to make the default usage of the constructor raise an exception:

class Foo:
    def __init__(self, arg, __private=False):
        assert __private, "Constructor shouldn't be called directly"
        self.arg = arg

    @classmethod
    def from_bar(cls, bar: Bar):
        return cls(bar.arg, __private=True)

    @classmethod
    def from_baz(cls, baz: Baz):
         return cls(baz.arg, __private=True)

Of course, anyone can still do Foo('abc', __private=True) but it would send a clear signal that such behavior isn't supported which follows the general design of Python.

妳是的陽光 2024-12-24 08:46:27

我知道,我迟到了,但我们可以这样做来创建私有构造函数。

class MyClass:
    __instance = None
    __private = False

    @staticmethod
    def get_instance():

        MyClass.__private = True
        __instance = MyClass()
        MyClass.__private = False
        return MyClass.__instance
     
    def __init__(self):

        if MyClass.__private == False:
             raise Exception("Its a private method. Please use get_instance method")
        

I know, I'm late but we can do something like this to create private constructor.

class MyClass:
    __instance = None
    __private = False

    @staticmethod
    def get_instance():

        MyClass.__private = True
        __instance = MyClass()
        MyClass.__private = False
        return MyClass.__instance
     
    def __init__(self):

        if MyClass.__private == False:
             raise Exception("Its a private method. Please use get_instance method")
        
你如我软肋 2024-12-24 08:46:27

另一种解决方案是让 __init__ 请求一种只有工厂方法才能知道的私有秘密

请参阅下面的示例:

from __future__ import annotations

class Foo:
    name: str

    def __init__(self, check_private_constructor: str):
        if check_private_constructor != "from_within":
            raise RuntimeError("Please use named constructors!")

    @staticmethod
    def from_name(name: str) -> Foo:
        r = Foo("from_within")
        r.name = name
        return r

    @staticmethod
    def from_index(index: int) -> Foo:
        r = Foo("from_within")
        r.name = str(index)
        return r
        
    def __str__(self):
        return "Foo.name=" + self.name


# a = Foo("eee")    # will raise an exception
a = Foo.from_name("Bob")
print(a)
b = Foo.from_index(3)
print(b)

Another solution, is to let __init__ ask for a kind of private secret that only factory methods will know.

See example below:

from __future__ import annotations

class Foo:
    name: str

    def __init__(self, check_private_constructor: str):
        if check_private_constructor != "from_within":
            raise RuntimeError("Please use named constructors!")

    @staticmethod
    def from_name(name: str) -> Foo:
        r = Foo("from_within")
        r.name = name
        return r

    @staticmethod
    def from_index(index: int) -> Foo:
        r = Foo("from_within")
        r.name = str(index)
        return r
        
    def __str__(self):
        return "Foo.name=" + self.name


# a = Foo("eee")    # will raise an exception
a = Foo.from_name("Bob")
print(a)
b = Foo.from_index(3)
print(b)
我不会写诗 2024-12-24 08:46:27

一个清晰且轻量级的解决方案是使构造函数参数成为必需的但“私有”。只有当您也将它们设置为仅限关键字时,这才真正有效,否则很容易错过

class Foo:
    def __init__(self, *, _x: int):
        self._x = _x

    @classmethod
    def create(cls, x: int) -> Foo:
        return Foo(_x=x)

这里,Foo(_x=1) 表明我们滥用了 API。

create 中的 Foo(_x=x) 是否违反了类自己的契约是有争议的。

A clear and lightweight solution is to make the constructor arguments required but "private". This only really works if you also make them keyword-only, else it's too easy to miss

class Foo:
    def __init__(self, *, _x: int):
        self._x = _x

    @classmethod
    def create(cls, x: int) -> Foo:
        return Foo(_x=x)

Here, Foo(_x=1) indicates we're misusing the API.

It's debatable whether Foo(_x=x) in create is breaking the class's own contract.

北恋 2024-12-24 08:46:27

我认为这可能是一种更直接的方法。

class Test:

    def __init__(self, value):
        self.value =  value
    
        caller_frame = inspect.stack()[1]
        caller_module = inspect.getmodule(caller_frame[0])
        caller_name = caller_frame.function
        assert caller_name == 'create', 'use Test.create instead'
        print(f'new Test instantiated using {caller_name}')
    
    @classmethod
    def create(cls, value):
        return Test(value)

现在,如果我调用,

> Test("example")

将返回以下 AssertionError

Traceback (most recent call last):
  ...
  File "<input>", line 1, in <module>
  File "<input>", line 9, in __init__
AssertionError: use Test.create instead

如果我调用,

> Test.create("example_two")

将返回以下内容:

new Test instantiated using create
<__main__.Test object at 0x00000275393E7BE0>

I think this is maybe a more straightforward approach.

class Test:

    def __init__(self, value):
        self.value =  value
    
        caller_frame = inspect.stack()[1]
        caller_module = inspect.getmodule(caller_frame[0])
        caller_name = caller_frame.function
        assert caller_name == 'create', 'use Test.create instead'
        print(f'new Test instantiated using {caller_name}')
    
    @classmethod
    def create(cls, value):
        return Test(value)

Now if I call

> Test("example")

The following AssertionError is returned

Traceback (most recent call last):
  ...
  File "<input>", line 1, in <module>
  File "<input>", line 9, in __init__
AssertionError: use Test.create instead

If I call

> Test.create("example_two")

The following is returned:

new Test instantiated using create
<__main__.Test object at 0x00000275393E7BE0>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文