0

My question is simple: How can I add properties and setter to classmethod ?

Here is my code:

class Ingredient():
     __max_stock = 100
     __stock = 0
     __prix = 0

     def __init__(self):
         pass

     @classmethod
     @property
     def prix(cls):
         return cls.__prix
     @classmethod
     @prix.setter
     def prix(cls, value):
         assert isinstance(value, int) and int(abs(value)) == value
         cls.__prix = value


Ingredient.prix = 10        #should be OK
Ingredient.prix = 'text'    #should raise an error
Ingredient.prix = 10.5      #should raise an error too

Problem is that the setter doesn't work when the var is a class variable. Here is the error I get :

AttributeError: 'classmethod' object has no attribute 'setter'

I use Python 3.x

nestarz
  • 99
  • 1
  • 10
  • does it have to be a `classmethod`? why not just have a default value passed to the constructor that you can change when needed? – Paul H Nov 15 '14 at 23:40
  • 1
    I'm not sure `property` and `classmethod` are *intended* to work together – chepner Nov 15 '14 at 23:44
  • First and foremost, thanks for having beautified my code. Second, the reason I want to keep classmethod is that the price is common to all ingredients. So when I want to change the price, all instances will have an updated price (and so on with stock and max_stock) – nestarz Nov 15 '14 at 23:45
  • @EliasRhouzlane, I think my answers accomplishes that. Can you test it out? – Paul H Nov 15 '14 at 23:48
  • 1
    I would just document that `Ingredient.prix` must be a non-negative integer. If the user sets it to something else, well, *caveat programmator*. You can't stop them from setting `Ingredient._Ingredient__prix` to something directly anyway. – chepner Nov 16 '14 at 00:16

2 Answers2

2

This is possible in python >= 3.9 per https://docs.python.org/3/howto/descriptor.html#id27 See For example, a classmethod and property could be chained together

You may have a use case where you only want to calculate a classmethod property one time then keep using that value. If that is the case and this calculation needs to be done on subclasses, one could use init_subclass in python >= 3.6 to do this

class A:
    @classmethod
    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        cls.prix = cls._prix

class B(A):
    _prix = 0
spacether
  • 2,136
  • 1
  • 21
  • 28
1

Don't use classmethod directly in this way. If you need a classproperty decorator that is analogous to the instance property decorator including the possibility of setters, check out other questions for some good patterns. (You could also do it with a metaclass, but there's probably no reason to get into that.)

Community
  • 1
  • 1
mvr
  • 611
  • 6
  • 5