如何创建一个 class 实例化仅在满足特定条件时才会发生?

How do I create a class where instantiation only happens if certain conditions are met?

假设我有这个 class:

class Person:
    def __init__(self, name):
        self.name = name

如果我想实例化Person我可以这样做:

me = Person("António")

但是,如果 name 的类型为 str,我只想实例化 Person 怎么办?
我试过这个:

class Person:
    def __init__(self, name):
        if type(name) == str:
            self.name = name

但是当我这样做的时候:

me = Person("António")
print(me.name)

you = Person(1)
print(you.name)

我明白了:

所以发生的一切是:

但我真正想要的是,如果名称不是 str,则停止所有实例化。
换句话说,我希望不可能使用非 str name.

Person class 创建对象

我该怎么做?

怎么样:

class Person:
    def __init__(self, name):
        if type(name) == str:
            self.name = name
        else: 
            raise Exception("name attribute should be a string")

您可以使用检查参数的工厂,如果一切正常,则 returns 使用 Person 对象,否则会引发错误:

也许是这样的:

class PersonNameError(Exception):
    pass

class Person:
    def __init__(self):
        self.name = None

def person_from_name(name: str) -> Person:
    """Person factory that checks if the parameter name is valid
    returns a Person object if it is, or raises an error without 
    creating an instance of Person if not.
    """
    if isinstance(name, str):
        p = Person()
        p.name = name
        return p
    raise PersonNameError('a name must be a string')

p = person_from_name('Antonio') 

鉴于:

p = person_from_name(123)   # <-- parameter name is not a string

抛出异常:

PersonNameError                           Traceback (most recent call last)
<ipython-input-41-a23e22774881> in <module>
     14 
     15 p = person_from_name('Antonio')
---> 16 p = person_from_name(123)

<ipython-input-41-a23e22774881> in person_from_name(name)
     11         p.name = name
     12         return p
---> 13     raise PersonNameError('a name must be a string')
     14 
     15 p = person_from_name('Antonio')

PersonNameError: a name must be a string

你应该使用 factory design pattern。您可以阅读更多相关信息 here。简单来说:

创建 Class/method 将检查条件和 return 新的 class 实例,仅当满足这些条件时。

如果您想修改实例化行为, 你可以创建一个构造函数, 使用 class 方法。

class Person:
    def __init__(self, name):
        self.name = name
        print("ok")

    @classmethod
    def create(cls, name):
        if not isinstance(name, str):
            raise ValueError(f"Expected name to be a string, got {type(name)}")
        return cls(name)
           
me = Person.create("António")
print(me.name)

you = Person.create(1)
print(you.name)

OK打印一次证明只实例化一次

ok
António
Traceback (most recent call last):
  File "/data/user/0/ru.iiec.pydroid3/files/accomp_files/iiec_run/iiec_run.py", line 31, in <module>
    start(fakepyfile,mainpyfile)  File "/data/user/0/ru.iiec.pydroid3/files/accomp_files/iiec_run/iiec_run.py", line 30, in start
    exec(open(mainpyfile).read(),  __main__.__dict__)
  File "<string>", line 17, in <module>
  File "<string>", line 11, in create
ValueError: Expected name to be a string, got <class 'int'>

[Program finished]

这里是正在进行的显式测试。 很少需要覆盖 new 并且对于日常正常的 classes 我认为应该避免。这样做可以使 class 实现变得简单。

class Test(object):
     print("ok")
     def __new__(cls, x):
         if isinstance(x, str) :           
             print(x)
         else:
             raise ValueError(f"Expected name to be a string, got {type(x)}")
 
obj1 = Test("António")

obj2 = Test(1)
ok
António
Traceback (most recent call last):
  File "/data/user/0/ru.iiec.pydroid3/files/accomp_files/iiec_run/iiec_run.py", line 31, in <module>
    start(fakepyfile,mainpyfile)  File "/data/user/0/ru.iiec.pydroid3/files/accomp_files/iiec_run/iiec_run.py", line 30, in start
    exec(open(mainpyfile).read(),  __main__.__dict__)
  File "<string>", line 14, in <module>
  File "<string>", line 10, in __new__
ValueError: Expected name to be a string, got <class 'int'>

[Program finished]