57

It is pretty easy to implement __len__(self) method in Python so that it handles len(inst) calls like this one:

class A(object):

  def __len__(self):
    return 7

a = A()
len(a) # gives us 7

And there are plenty of alike methods you can define (__eq__, __str__, __repr__ etc.). I know that Python classes are objects as well.

My question: can I somehow define, for example, __len__ so that the following works:

len(A) # makes sense and gives some predictable result
Milimetric
  • 13,411
  • 4
  • 44
  • 56
ikostia
  • 7,395
  • 6
  • 30
  • 39
  • 13
    "Can I?" or "Should I?" – wim Oct 04 '11 at 02:04
  • 5
    Perhaps you could elaborate some on your intended use-case, because I'm failing to imagine any situation where this could be useful. It might help us give you an idea of how to solve this better (if there is a better way), or at least help us help you accomplish your goal better. – Chris Lutz Oct 04 '11 at 02:13
  • 5
    @ChrisLutz What if the class kept track of how many instances were instantiated of it, or had a list of all its instances? – agf Oct 04 '11 at 04:24
  • I'd argue using a static field/property/member that is of an enumerable type, used like `len(TheClass.instances)`, is far more readable and intuitive to the next developer than `len(TheClass)`. I'd intuitively expect that `len(TheClass)` either gives an error or counts the number of fields/properties/members of the class itself, behaving something like `len(TheClass.__dict__.keys())` – iono Feb 03 '21 at 14:30

4 Answers4

65

What you're looking for is called a "metaclass"... just like a is an instance of class A, A is an instance of class as well, referred to as a metaclass. By default, Python classes are instances of the type class (the only exception is under Python 2, which has some legacy "old style" classes, which are those which don't inherit from object). You can check this by doing type(A)... it should return type itself (yes, that object has been overloaded a little bit).

Metaclasses are powerful and brain-twisting enough to deserve more than the quick explanation I was about to write... a good starting point would be this stackoverflow question: What is a Metaclass.

For your particular question, for Python 3, the following creates a metaclass which aliases len(A) to invoke a class method on A:

class LengthMetaclass(type):

    def __len__(self):
        return self.clslength()

class A(object, metaclass=LengthMetaclass):

    @classmethod
    def clslength(cls):
        return 7

print(len(A))

(Note: Example above is for Python 3. The syntax is slightly different for Python 2: you would use class A(object):\n __metaclass__=LengthMetaclass instead of passing it as a parameter.)

The reason LengthMetaclass.__len__ doesn't affect instances of A is that attribute resolution in Python first checks the instance dict, then walks the class hierarchy [A, object], but it never consults the metaclasses. Whereas accessing A.__len__ first consults the instance A, then walks it's class hierarchy, which consists of [LengthMetaclass, type].

Eli Collins
  • 8,375
  • 2
  • 34
  • 38
  • Note that in Python 3 a metaclass would be defined like `class A(object, metaclass=LengthMetaclass):` – Ant Feb 24 '19 at 19:05
  • This is not normally what you want, because it does not use the object's instance variables, but returns the same value for all instances. Normally you would just want to implement __len__ in your class. See [this article](https://realpython.com/operator-function-overloading/#giving-a-length-to-your-objects-using-len). – Richard Whitehead Mar 11 '19 at 09:55
5

Since a class is an instance of a metaclass, one way is to use a custom metaclass:

>>> Meta = type('Meta', (type,), {'__repr__': lambda cls: 'class A'})
>>> A = Meta('A', (object,), {'__repr__': lambda self: 'instance of class A'})
>>> A
class A
>>> A()
instance of class A
Eryk Sun
  • 33,190
  • 5
  • 92
  • 111
0

I fail to see how the Syntax specifically is important, but if you really want a simple way to implement it, just is the normal len(self) that returns len(inst) but in your implementation make it return a class variable that all instances share:

class A:
    my_length = 5
    def __len__(self):
        return self.my_length

and you can later call it like that:

len(A()) #returns 5

obviously this creates a temporary instance of your class, but length only makes sense for an instance of a class and not really for the concept of a class (a Type object).

Editing the metaclass sounds like a very bad idea and unless you are doing something for school or to just mess around I really suggest you rethink this idea..

-6

try this:

class Lengthy:
    x = 5
    @classmethod
    def __len__(cls):
        return cls.x

The @classmethod allows you to call it directly on the class, but your len implementation won't be able to depend on any instance variables:

a = Lengthy()
len(a)
Ankhara33
  • 13
  • 1