检查self.__class__的目的是什么?

37

检查self.__class__的目的是什么?我发现有些代码创建了一个抽象接口类,然后检查它的self.__class__是否为自身,例如:

class abstract1 (object):
  def __init__(self):
    if self.__class__ == abstract1: 
      raise NotImplementedError("Interfaces can't be instantiated")

这是什么目的?是为了检查类是否属于自身类型吗?

这段代码来自NLTK的http://nltk.googlecode.com/svn/trunk/doc/api/nltk.probability-pysrc.html#ProbDistI


1
我认为这需要更多的上下文。你在哪里“找到”这段代码?它基本上是有问题的。 - Iguananaut
1
@Iguananaut:为什么它坏了?当作为抽象基类使用时,它的工作非常好。 - Martijn Pieters
1
我很确定之前它是不同的东西?我不知道——我同意,现在的东西没有问题。 - Iguananaut
6个回答

51

self.__class__是对当前实例的类型的引用。

对于abstract1的实例,它将是abstract1本身,这不是您想要的抽象类。抽象类只能被继承,不能直接创建实例:

>>> abstract1()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in __init__
NotImplementedError: Interfaces can't be instantiated

对于abstract1的一个子类实例self.__class__将引用特定的子类:

>>> class Foo(abstract1): pass
... 
>>> f = Foo()
>>> f.__class__
<class '__main__.Foo'>
>>> f.__class__ is Foo
True

在这里抛出异常就像在代码的其他地方使用断言语句一样,它可以保护您免受犯傻瓜错误的困扰。
请注意,测试实例类型的“Pythonic”方式是使用type()函数,结合使用is运算符进行身份测试:
class abstract1(object):
    def __init__(self):
        if type(self) is abstract1: 
            raise NotImplementedError("Interfaces can't be instantiated")

type() 应该优先于 self.__class__,因为后者可能会被类属性遮盖

在这里使用等式测试没有多大意义,因为对于自定义类,__eq__ 基本上实现为一个身份测试。

Python 还包括一个标准库来定义抽象基类,称为abc。它允许您将方法和属性标记为抽象,并拒绝创建任何尚未重新定义这些名称的子类的实例。


因为 Python 中的所有东西都是对象,类型也是一个对象。因此,普通对象的 __class__ 指向其类的类型 object。而类型对象的 __class__ 则指向元类 object - smwikipedia

1
在Python 3中,使用type()__class__检查类型将返回相同的结果。
class C:pass
ci=C()
print(type(ci)) #<class '__main__.C'>
print(ci.__class__) #<class '__main__.C'>

我最近检查了与@dataclass装饰器相关的实现(Raymond Hettinger直接参与了该项目),他们使用__class__来引用类型。

所以使用__class__并不是错误的 :)


1
您发布的代码是无操作的; self.__class__ == c1 不是条件语句的一部分,因此布尔值被评估,但结果没有用于任何操作。
您可以尝试创建一个抽象基类,检查self.__class__是否等于抽象类,而不是假设的子类(通过if语句),以防止由于开发人员错误而实例化抽象基类本身。

1
尽管对于更近版本的 Python,我们应该使用抽象基类 abc.ABCMeta - Iguananaut
仍然可以直接实例化一个ABC。 - Martijn Pieters

1

这是为了什么?是为了检查类是否属于自身类型吗?

是的,如果你尝试构造一个类型为 Abstract1 的对象,它会抛出异常告诉你不允许这样做。


0

线索在类名“abstract1”和错误中。这是一个抽象类,意味着它旨在被子类化。每个子类将提供自己的行为。抽象类本身用于记录接口,即实现接口的类应具有的方法和参数。它不是要被实例化的,测试用于告诉我们是否在类本身或子类中。

请参阅Julien Danjou的文章中关于抽象类的部分。


0
这对于不可变的静态字段非常有用。没有.__class__.它是一个不同的变量。不需要使用@classmethod
class NoClass:
    bo: bool = False

    def makeTrue(self):
        self.bo = True

no_class = NoClass()
print(f"before: {NoClass.bo=}")  # before: NoClass.bo=False
no_class.makeTrue()
print(f"after:  {NoClass.bo=}")  # after:  NoClass.bo=False


class WithClass:
    bo: bool = False

    def makeTrue(self):
        self.__class__.bo = True


with_class = WithClass()
print(f"before: {WithClass.bo=}")  # before: WithClass.bo=False
with_class.makeTrue()
print(f"after:  {WithClass.bo=}")  # after:  WithClass.bo=True

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接