简体   繁体   中英

What is the pythonic way to overload class variables(properties)?

Hello!

I need each child class to has own set of constants. I've found a "proper" way with properties and overloading setter methods, but:

  1. I need to define constructor in child classes (which I don't need) and assign values in constructor;
  2. Every instance of class will have copy of this constants in memory (senseless resource consumption);
  3. It looks weird when you define setter, getter and property at all just to use it as constant.

I've done something like this:

class BaseClass:
    def get_a(self):
        raise NotImplementedError("Oooops")

    def get_b(self):
        raise NotImplementedError("Oooops")

class FirstClass(BaseClass):
    def get_a(self):
        return "a"

    def get_b(self):
        return "b"

class SecondClass(BaseClass)
    def get_a(self):
        return "A"

    def get_b(self):
        return "B"

class SomeClass:
    def some_method(self, class_param):
        return "{}-{}".format(class_param.get_a, class_param.get_b)

This method also doesn't solve problems of method with properties (except last), just more compact. There's other way, which I find not good:

class BaseClass:
    pass

class FirstClass(BaseClass):
    A_CONST = "a"
    B_CONST = "b"

class SecondClass(BaseClass)
    A_CONST = "A"
    B_CONST = "B"

class SomeClass:
    def some_method(self, class_param):
        return "{}-{}".format(class_param.A_CONST, class_param.B_CONST)

In fact, it solve all problems and pretty compact, BUT it violates rule of inheritance ( isn't it? ).

Question : What is the proper way to do this?

PS Provided code is simplified example, base class contains methods which I use in child class, please don't write me that base class is useless here.

If you want your base class to indicate that it needs to be subclassed with certain attributes, you can make it an abstract base class .

from abc import ABC, abstractmethod

class Base(ABC):
    @property
    @abstractmethod
    def a(self):
        raise NotImplementedError
    @property
    @abstractmethod
    def b(self):
        raise NotImplementedError

You will then not be allowed to instantiate Base or its subclasses unless they override the abstract methods. You can do either

class First(Base):
    a = 1
    b = 2

to assign class attributes with those names, or

class Second(Base):
    @Base.a.getter
    def a(self):
        return 3
    @Base.b.getter
    def b(self):
        return 4

The benefit of the second approach is that it will raise an error if you try to assign to the property

Second().a = 5  # AttributeError

your second version looks fine to me… each language has their own conventions around what a "class" or "object" means, and this looks reasonably "Pythonic"

one minor comment about the first version, is that Python doesn't care about "overloading", you don't need to include:

class BaseClass:
    def get_a(self):
        raise NotImplementedError("Oooops")

at all, ie it's fine to have:

class BaseClass:
    pass

as well in your first version.

another potentially useful tool here is the property decorator, eg:

class FirstClass(BaseClass):
    @property
    def a(self):
        return "a"

print(FirstClass().a)

would output " a "

If the key_name : [A_CONST, B_CONST] remains same for child classes, super() will take care of all your concerns ( 1., 2., 3. ).

A 'pythonic' solution would include, to remove duplication's, of any, setter and getter in child classes and let BaseClass() handle these common-tasks.

class BaseClass(object):
    def __init__(self, a, b):
        self._a_const = a
        self._b_const = b
    @property
    def A_CONST(self):
      return self._a_const
    @property
    def B_CONST(self):
      return self._b_const

class FirstClass(BaseClass):
    def __init__(self, _aconst, _bconst):
        # Let Base class object hold my constants but FirstClass Constructor
        # is setting the value. Look SecondClass
        super(FirstClass, self).__init__(_aconst, _bconst)

class SecondClass(BaseClass):
    def __init__(self, _aconst, _bconst):
        # Magic happens here
        super(SecondClass, self).__init__(_aconst, _bconst)

class SomeClass():
    def some_method(self, class_param):
        return "{}-{}".format(class_param.A_CONST, class_param.B_CONST)

firstobj  = FirstClass("a", "b")
secondobj = SecondClass("A", "B")

print(SomeClass().some_method(firstobj))
print(SomeClass().some_method(secondobj))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM