The documentation on __slots__ says they're "implemented as descriptors." Is it possible to customize the getters/setters for descriptors created via __slots__? Can I do something like
class Foo(object):
__slots__ = ['a']
def __init__(self, a):
self._a = a
#property
def a(self):
return self._a.title()
#a.setter
def a(self, x):
if len(x) == 10:
self._a = x
else:
raise ValueError('incorrect length!')
#a.deleter
def a(self):
self._a = ''
ETA: Only semi-relatedly, the self._a = a bit above would mean that the initial value of a wouldn't be run through the setter. Is there a way to pass the value into the setter on __init__ as well?
ETA2: So based on Bi Rico's answer, I worked out this:
class Foo(object):
__slots__ = ('_a',)
def __init__(self, x):
self._a = self.validate_a(x)
#staticmethod
def validate_a(x):
if x % 2 == 0:
return x
else:
raise ValueError(':(')
#property
def a(self):
return str(self._a)
#a.setter
def a(self, x):
self._a = self.validate(x)
#a.deleter
def a(self):
self._a = 0
The separate validate_a method solves my 'add-on' question about treating the value(s) passed into __init__ the same as values passed in through the setter (and as such isn't necessary if you don't want to do that.)
It feels a little hacky to only put 'dummy' var names in __slots__ (i.e., Foo._a only exists for the benefit of the Foo.a property), but it works.
Your code almost works as is; the only changes you need to make are
__slots__ = ['_a'] # _a instead of a
def __init__(self, a):
self.a = a # a instead of _a
If for some reason you really want to avoid the separate _a slot and a wrapper, you can replace the default slot descriptor with your own descriptor, but I wouldn't recommend it. Also, the replacement has to happen after the class is created:
class Foo(object):
__slots__ = ['a']
underlying_descriptor = Foo.a
#property
def a(self):
return underlying_descriptor.__get__(self, Foo).title()
#a.setter
def a(self, x):
if len(x) == 10:
underlying_descriptor.__set__(self, x)
else:
raise ValueError('incorrect length!')
#a.deleter
def a(self):
underlying_descriptor.__del__(self)
Foo.a = a
You might try to simplify all that underlying_descriptor stuff by setting Foo._a = Foo.a and accessing it through self._a, but then you have an _a attribute on your objects, and the whole point of this version is to avoid the second attribute.
You don't need to list properties in __slots__ only attributes, if you change slots to __slots__ = ['_a'], your class will work as expected.
Update
Sorry I didn't see you add-on question in my first read through. Having a static validate method is fine, but you don't need to call it explicitly in the in __init__, instead set self.a directly, only use self._a when you want to intentionally bypass the setter/getter.
I don't know what you mean by,
It feels a little hacky to only put 'dummy' var names in __slots__
a is a property of the class (which btw is also implemented using descriptors) so if you also include it in __slots__ you're instructing the class to create two conflicting descriptors for the same thing.
_a is an attribute of the class so if you're using __slots__ it must be there. Just because _a starts with an underscore doesn't make it any better or worse than any other attribute, I would call it a "protected" attribute instead of a dummy attribute :). Are you sure you want to use __slots__ at all, that seems to be the most "hacky" part of this whole thing.
Absolutely. You just reference the property and use your setter in your init. Make your class follow the same rules internally that your users are expected to follow, at least when __init__ is taking input like this.
class Foo(object):
__slots__ = ['_a']
def __init__(self, a):
self.a = a
#property
def a(self):
return self._a
#a.setter
def a(self, x):
if len(x) == 10:
self._a = x
else:
raise ValueError('incorrect length!')
#a.deleter
def a(self):
self._a = ''
This way, if someone instantiates with an "incorrect length" they'll hit the same error path as setting object values.
You absolutely don't need the static method, that's a major use case of using setters in the first place, to validate or sanitize the data before accepting it! A reason to use a method like that is if you have multiple properties that all need the same scrubbing. e.g. Foo.b, Foo.c, Foo.d all work like Foo.a. In which case, you'd also want to indicate that it's private with a leading underscore: def _validate_nums(x):
For your new code, it's exactly the same as the old, no need for a static method:
class Foo(object):
__slots__ = ('_a',)
def __init__(self, x):
self.a = x
#property
def a(self):
return str(self._a)
#a.setter
def a(self, x):
if x % 2 == 0:
self._a = x
else:
raise ValueError(':(')
#a.deleter
def a(self):
self._a = 0
A reason you might break from this is if you want an explicit undefined or default value, that your class normally prohibits (such as your original class prohibits any length that's not 10). In which case you could do:
def __init__(self, a="four"):
if a == "four":
self._a = "four"
else:
self.a = a
This example is a little weird, you'd probably use None, 0, an empty value (like [] or ()), or something else to test against elsewhere, like you do with your newer code, which doesn't need this.
It feels a little hacky to only put 'dummy' var names in slots (i.e., Foo._a only exists for the benefit of the Foo.a property), but it works.
I think you are looking at it backwards, but it makes sense since without directors we all would prefer to use Foo.a over Foo._a. However, Foo._a is your class attribute. It's what your class uses. It's the property decorators that allow Foo.a that are a little bit hacky. They are defensive measures to protect your class from misuse or bad behavior. They aren't your class attribute at all.
By also using _a instead of like internal_data you communicate to other users: _a is meant to be private, access it directly at your own risk, use the property decorators if you want expected behavior!
Related
In Python 2.7, I have the following example classes defined:
class A:
def __init__(self):
self.a = 1
self.b = 3
class B(A):
def __init__(self):
A.__init__(self)
self.a = 1
obj_b = B()
print(vars(obj_b))
Object obj_b now has both a and b. Although a has the same value as in class A, it's been reassigned in class B.
Is there any way to tell if a is reassigned in class B?
yeah, I see what you mean, thanks, so is there any way to check if such reassignment happened in class B's constructor?
Answering a question from the comments, this can certainly be done. It's not foolproof, in the sense that a programmer can go out of their way to cheat your system, but they have to do so on purpose.
We just need to make a a property.
class A:
def __init__(self):
self._a = 1
self.was_a_changed = False
#property
def a(self):
return self._a
#a.setter
def a(self, x):
self._a = x
self.was_a_changed = True
class B(A):
def __init__(self):
self.a = 1
class C(A):
pass
obj_a = A()
obj_b = B()
obj_c = C()
print(obj_a.was_a_changed) # False
print(obj_b.was_a_changed) # True
print(obj_c.was_a_changed) # False
Now a isn't a real value; it's a property which is backed by the actual value _a. But if a subclass reassigns a, it'll make a note so that we can check whether it's been touched later.
If someone wants to cheat your system, they can always assign directly to self._a rather than self.a, but that would be unidiomatic Python to assign to a private you don't own anyway.
If a is a class attribute, rather than an instance attribute, this question would make more sense. An instance of A (possibly a subclass) could compare self.__class__.a against A.a. As is you could potentially compare self.a against A().a, but there's no way to distinguish between changes made during initialization and changes made at any other point.
I have a custom class whose instances are initialized with several attributes foo, bar, ... that are numeric values or strings. These attributes are used by several class methods to calculate additional attributes of the class instance. I would like to define these attributes as properties, so that when changing them, the additional attributes are recalculated. The following works:
class MyClass:
def __init__(self,foo,bar):
self._foo = foo
self._bar = bar
#property
def foo(self):
return self._foo
#foo.setter
def foo(self,new_foo):
old_foo = self._foo:
self._foo = new_foo
if old_foo != new_foo:
self._force_recalc()
#property
def bar(self):
return self._bar
#bar.setter
def bar(self,new_bar):
old_bar = self._bar:
self._bar = new_bar
if old_bar != new_bar:
self._force_recalc()
def _force_recalc(self)
# call some methods that recalculate attributes
However, two aspects strike me as inelegant:
The setter methods are basically copy-paste with only the variable names adjusted. But I couldn't come up with a solution that makes use of a common setter method.
Is there a way to avoid old_foo and old_bar? They result from the fact that _force_recalc must be called after foo and bar were reset, but only if their value changed.
I first tried the solution suggested by #Barmar. This works, but lead to the problem of an unnecessary amount of recalculations during initialization of all the properties. Addtionally, they still had to be defined individually and I was unable to check whether the value actually changed.
Following #juanpa.arrivillaga's advice, I defined my own decorator class and ended up with something like this:
class MyClass:
class _MyProperty:
def __init__(self, value=None):
self.value = value
def __get__(self, instance, owner=None):
return self.value
def __set__(self, instance, value):
if value != self.value:
self.value = value
instance._force_recalc()
#classmethod
def _set_init_state(cls,values):
cls.foo,cls.bar = [cls._MyProperty(value) for value in values]
def __init__(self,foo,bar):
self._set_init_state((foo,bar))
self._force_recalc()
def _force_recalc(self)
# call some methods that recalculate attributes
While this definitely looks less repetitive, it can probably be argued that the former was more explicit. However, I like it and it solves my original request.
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:
I need to define constructor in child classes (which I don't need) and assign values in constructor;
Every instance of class will have copy of this constants in memory (senseless resource consumption);
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?
P.S. 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, i.e. it's fine to have:
class BaseClass:
pass
as well in your first version.
another potentially useful tool here is the property decorator, e.g:
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))
In the case of multiple inheritance in python, is there a way to identify which super class a class-level variable is obtained from?
All attempts I tried to google are overwhelmingly about How to get the attribute not find out where it came from:
https://www.google.com/search?q=pythin+which+super+class+defines+attr
https://www.google.com/search?q=python+which+super+class+has+attribute&oq=python+which+super+class+has+attr
https://www.google.com/search?q=python+which+super+class+attribute+obtained+from
I suppose I can manually step through the MRO using inspect.getmro(cls). But I couldn't find any more elegant solutions. Just wondering if anyone knows of one.
EDIT
For a concrete example:
class Super1(object):
__class_attribute__ = "Foo"
class Super2(object):
pass
class Derived(Super1, Super2):
pass
d = Derived()
parent_cls = some_function_to_get_defining_class(d.__class_attribute__) # <-- should return `Super1`
The __qualname__ attribute gives an indication from which class a method was inherited. However, this only returns a string, not the superclass itself. If you need to the superclass for metaprogramming, I think you are going to have to dig into the MRO.
class A:
def a(self):
return 1
def b(self):
return 2
class B:
def b(self):
return 2.5
def c(self):
return 3
class C(A,B):
pass
Using:
C.b.__qualname__
# returns:
'A.b'
However, this does not apply when using abstract methods to define an interface, since the method has to be overwritten.
from abc import abstractmethod
class A:
def a(self):
return 1
#abstractmethod
def b(self):
pass
class C(A):
def b(self):
return 100
C.b.__qualname__
# returns:
'C.b'
I have two classes with a common function f, like here:
class ClassA(object):
def f(self, var):
self.A = g(var, self.A)
# specific code for ClassA, also changing self.A
class ClassB(object):
def f(self, var):
self.B = g(var, self.B)
# specific code for ClassB, also changing self.B
In both classes, f does the same on two variables called A and B, which are structurally equivalent. I would like to put f it into an abstract class to avoid code duplication. One way to do this is
class ClassX(object):
def f(self, var):
self.X = g(var, self.X)
class ClassA(ClassX):
# specific code for ClassA, also changing self.X
class ClassB(ClassX):
# specific code for ClassB, also changing self.X
In this solution the variables A and B have been renamed X. However, to make my code more self-explaining, I would like to keep those specific names (say A,B) for X in the special classes.
Is there a way to do this?
Also please comment if you can suggest a more meaningful and descriptive title, so it becomes more valuable to the community.
Edit: The solution should also work if the variables A, B take a type which is pass-by-value and should assume that both their value and type might be changed from outside the class during the program execution.
First, a caveat: if your case actually calls for inheritance of the kind you describe, then the variable name should probably be the same. In most cases, giving different names to attributes that are identical in two related classes doesn't make code more self-explaining -- it makes it less self-explaining. It makes the attributes look different when they're really the same, adding complexity and potential for confusion.
However, I can imagine a few cases where you might want to do something like this -- it sounds a bit like you want to define a common interface to two different kinds of objects. One simple approach would be to use properties to define aliases; so in each class, you'd have different attributes (A and B), but you'd also define a property, X, that would access the appropriate attribute in either case. So in ClassA, you'd do something like this in the class definition:
#property
def X(self):
return self.A
#x.setter
def X(self, value):
self.A = value
#x.deleter
def X(self):
del self.A
And in ClassB:
#property
def X(self):
return self.B
#x.setter
def X(self, value):
self.B = value
#x.deleter
def X(self):
del self.B
Then, you could define a common function that would work only with X, which subclasses could inherit, overriding property X in whatever way is appropriate.
This is rarely worth the additional layer of indirection though. Most of the time, if there's a good reason for two classes to share a method, then they should also share the attributes that the method changes, name and all.
If all you want to do is alias an instance attribute, you could just use a property.
class A(object):
def f(self, var):
self.x = g(var, self.x)
#property
def x(self):
return getattr(self, self.x_alias)
#x.setter
def x(self, val):
setattr(self, self.x_alias, val)
class AA(A):
x_alias = 'aval'
def __init__(self, aval):
self.aval = aval
class AB(A):
x_alias = 'bval'
def __init__(self, bval):
self.bval = bval