0

I have this code of a class that contains a list _offers of custom Offer objects. For this list I created a property to allow access to it:

class OffersManager:    
    _offers: list[Offer] = []
    
    @property
    def offers(cls) -> list[Offer]:
        return cls._offers

The problem is that the program treats this list as a "property" object, so it gives me an error when I try to get the length of the list:

>>> len(OffersManager.offers)
>>> Traceback (most recent call last):
      File "<pyshell#1>", line 1, in <module>
        len(OffersManager.offers)
    TypeError: object of type 'property' has no len()

Can someone tell me the correct way to make a property return the attribute as a list?

Itay Dvash
  • 40
  • 9

2 Answers2

2

Update from the comments: this is deprecated and may not work correctly, use the metaclass approach from SUTerliakov's answer instead.


You need to mark the property as a class method, so it's bound to the class itself rather than the instances:

class OffersManager:    
    _offers: list[OfferData] = []
    
    @classmethod
    @property
    def offers(cls) -> list[OfferData]:
        return cls._offers
yut23
  • 2,624
  • 10
  • 18
  • Thank you so much! I will emphasize to those who will encounter the problem in the future, that the order of the decorators is important (I tried to reverse them and got the same error). – Itay Dvash May 07 '23 at 20:31
  • 2
    This isn't recommended; the machinery that lets you define "class" properties is deprecated; the documentation suggests it has been removed from Python 3.11 (though it still seems to work in that version). – chepner May 07 '23 at 21:05
  • Interesting, I didn't know that! For future reference, the Python 3.11 [release notes](https://docs.python.org/3.11/whatsnew/3.11.html#language-builtins) entry. – yut23 May 07 '23 at 22:25
1

To avoid deprecated functionality which never used to work properly, you can define your property on a metaclass:

from __future__ import annotations
from typing import TypeAlias

OfferData: TypeAlias = int


class OffersManagerMeta(type):
    _offers: list[OfferData] = []
    
    @property
    def offers(cls) -> list[OfferData]:
        return cls._offers


class OffersManager(metaclass=OffersManagerMeta):
    pass


print(OffersManager.offers)
OffersManager._offers = [1, 2]
print(OffersManager.offers)

And this even passes strict mypy.

STerliakov
  • 4,983
  • 3
  • 15
  • 37