What is the purpose of checking self.__class__?
Asked Answered
L

6

39

What is the purpose of checking self.__class__ ? I've found some code that creates an abstract interface class and then checks whether its self.__class__ is itself, e.g.

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

What is the purpose of that? Is it to check whether the class is a type of itself?

The code is from NLTK's http://nltk.googlecode.com/svn/trunk/doc/api/nltk.probability-pysrc.html#ProbDistI

Lassa answered 15/12, 2013 at 20:27 Comment(3)
I think this requires more context. Where did you "find" this code? It's basically broken.Bryozoan
@Iguananaut: Why is it broken? It works fine when used as intended; as an abstract base class.Cariole
I'm pretty sure earlier it was something different? I don't know--I agree, what's on there now is not broken.Bryozoan
C
52

self.__class__ is a reference to the type of the current instance.

For instances of abstract1, that'd be the abstract1 class itself, which is what you don't want with an abstract class. Abstract classes are only meant to be subclassed, not to create instances directly:

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

For an instance of a subclass of abstract1, self.__class__ would be a reference to the specific subclass:

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

Throwing an exception here is like using an assert statement elsewhere in your code, it protects you from making silly mistakes.

Note that the pythonic way to test for the type of an instance is to use the type() function instead, together with an identity test with the is operator:

class abstract1(object):
    def __init__(self):
        if type(self) is abstract1: 
            raise NotImplementedError("Interfaces can't be instantiated")

type() should be preferred over self.__class__ because the latter can be shadowed by a class attribute.

There is little point in using an equality test here as for custom classes, __eq__ is basically implemented as an identity test anyway.

Python also includes a standard library to define abstract base classes, called abc. It lets you mark methods and properties as abstract and will refuse to create instances of any subclass that has not yet re-defined those names.

Cariole answered 19/12, 2013 at 17:52 Comment(1)
Because everything in python is an object, a type is also an object. So a normal object's __class__ points to the type object of its class. And the type object's __class__ points to the meta class object.Chink
K
1

The code that you posted there is a no-op; self.__class__ == c1 is not part of a conditional so the boolean is evaluated but nothing is done with the result.

You could try to make an abstract base class that checks to see if self.__class__ is equal to the abstract class as opposed to a hypothetical child (via an if statement), in order to prevent the instantiation of the abstract base class itself due to developer mistake.

Kedron answered 15/12, 2013 at 20:32 Comment(2)
Although for more recent versions of Python one should just use an abstract base class using abc.ABCMeta.Bryozoan
An ABC can still be instantiated directly.Cariole
A
1

What is the purpose of that? Is it to check whether the class is a type of itself?

Yes, if you try to construct an object of type Abstract1 it'll throw that exception telling you that you're not allowed to do so.

Alfons answered 19/12, 2013 at 16:2 Comment(0)
S
1

In Python 3 either using type() to check for the type or __class__ will return the same result.

class C:pass
ci=C()
print(type(ci)) #<class '__main__.C'>
print(ci.__class__) #<class '__main__.C'>

I recently checked the implementation for the @dataclass decorator (Raymond Hettinger is directly involved into that project), and they are using __class__ to refer to the type.

So it is not wrong to use __class__ :)

Stilu answered 5/6, 2019 at 10:1 Comment(0)
M
0

The clues are in the name of the class, "abstract1", and in the error. This is intended to be an abstract class, meaning one that is intended to be subclassed. Each subclass will provide its own behaviour. The abstract class itself serves to document the interface, i.e. the methods and arguments that classes implementing the interface are expected to have. It is not meant to be instantiated itself, and the test is used to tell whether we are in the class itself or a subclass.

See the section on Abstract Classes in this article by Julien Danjou.

Micamicaela answered 19/12, 2013 at 15:59 Comment(0)
P
0

This is useful for immutable static fields. Without .__class__. it's a different varaible. No need to use @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
Pinkeye answered 2/8, 2023 at 9:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.