I am trying to subclass a python class and overwrite a regular attribute with a #property function. The catch is that I can't modify the parent class, and the api for the child class needs to look the same as the parent class (but behave differently). (So my question is different from this one in which the parent class also used a #property method to access the underlying attribute.)
The simplest possible example is
# assume this class can't be overwritten
class Parent(object):
def __init__(self, a):
self.attr = a
# how do I make this work?
class Child(Parent):
def __init__(self, a):
super(Child, self).__init__(a)
# overwrite access to attr with a function
#property
def attr(self):
return super(Child, self).attr**2
c = Child(4)
print c.attr # should be 16
This produces an error when the parent init method is called.
<ipython-input-15-356fb0400868> in __init__(self, a)
2 class Parent(object):
3 def __init__(self, a):
----> 4 self.attr = a
5
6 # how do I make this work?
AttributeError: can't set attribute
Hopefully it is clear what I want to do and why. But I can't figure out how.
This is easily fixed by adding a setter method
class Child(Parent):
def __init__(self, a):
self._attr = None
super(Child, self).__init__(a)
# overwrite access to a with a function
#property
def attr(self):
return self._attr**2
#attr.setter
def attr(self, value):
self._attr = value
Related
Please explain why I cannot use super(class, self) to access the properties defined in the class higher in the class inheritance hierarchy, and how to access them.
According to the document, super(class, self should be returning a proxy object via which I can access the def name() in a parent class instance.
super([type[, object-or-type]])
Return a proxy object that delegates method calls to a parent or
sibling class of type. This is useful for accessing inherited methods
that have been overridden in a class.
The object-or-type determines the method resolution order to be
searched. The search starts from the class right after the type.
For example, if mro of object-or-type is D -> B -> C -> A ->
object and the value of type is B, then super() searches C -> A ->
object.
I thought super(Parent) will give a proxy to the GrandParent object which has the def name().
class GrandParent:
def __init__(self):
self._name = "grand parent"
#property
def name(self):
return self._name
class Parent(GrandParent):
def __init__(self):
super().__init__()
self._name = "parent"
#property
def name(self):
return super().name
class Child(Parent):
def __init__(self):
super().__init__()
self._name = "child"
#property
def name(self):
return super(Parent).name
print(Child().name)
---
AttributeError: 'super' object has no attribute 'name'
Without (class, self), it returns ... the child property. Obviously I have not understood how self and super work in Python. Please suggest what resources to look into to fully understand the behavior and the design.
class GrandParent:
def __init__(self):
self._name = "grand parent"
#property
def name(self):
return self._name
class Parent(GrandParent):
def __init__(self):
super().__init__()
self._name = "parent"
#property
def name(self):
return super().name
class Child(Parent):
def __init__(self):
super().__init__()
self._name = "child"
#property
def name(self):
return super().name
print(Child().name)
---
child
The self always refers to one and the same object. When you do super().__init__(), the self in the parent's __init__ is your instance of Child. GrandParent.__init__ just sets an attribute on that object. By chaining all those __init__s, you're in effect just doing this:
o = object()
o._name = 'grand parent'
o._name = 'parent'
o._name = 'child'
You're just overwriting the _name attribute, of which there's only one. All the different #propertys just return the value of this one _name attribute, of which your object only has one, and whose value is 'child'.
If you want your object to have a separate _name attribute per parent, you will actually have to create separate attributes. The easiest way is probably with Python's double-underscore name mangling a.k.a. "private attributes":
>>> class A:
... def __init__(self):
... self.__foo = 'bar'
... #property
... def foo(self):
... return self.__foo
...
>>> class B(A):
... def __init__(self):
... super().__init__()
... self.__foo = 'baz'
... #property
... def foo(self):
... return super().foo
...
>>> B().foo
'bar'
>>> vars(B())
{'_A__foo': 'bar', '_B__foo': 'baz'}
The actual attributes are named _A__foo and _B__foo and thereby don't conflict with each other.
When working with python instances, it is possible to access bound methods of the same class using self. This resolves to a method corresponding to the same class in hierarchy.
class A:
def f(self):
return 1
def __init__(self):
self.v = self.f()
class B(A):
def f(self):
return 2
b = B()
# b.v is set to 2
But, when working with class methods, there is no apparent way of accessing methods of the same class as above.
In my use case, f above needs to be a class method and I need to set class variable v according to f corresponding to the same class. Somewhat like:
class A:
#classmethod
def f(cls):
return 1
v = resolution_of_calling_class.f()
class B(A):
#classmethod
def f(cls):
return 2
# B.v should be 2
edit: v is actually an attribute defined by parent class, which should find a method overridden by child class
You just need to override __new__ method, since it is invoked before the __init__ and its purpose is to create an instance, that will be initialized by __init__.
class A:
def __new__(cls, *args, **kwargs):
cls.v = cls.f()
return super().__new__(cls, *args, **kwargs)
#classmethod
def f(cls):
return 1
class B(A):
#classmethod
def f(cls):
return 2
a = A()
print(a.v)
b = B()
print(b.v)
1
2
I am not 100% sure I understand what you are trying to do.
I used your code above and
class A:
#classmethod
def f(cls):
return 1
class B:
#classmethod
def f(cls):
return 2
print(B.f())
gives me 2 just as I expected it would. Should B be a child class of A as in the first example?
I have this two classes that I needed to divide, though they share some common features. Is it possible to access first class' self.value from the second class? Please look at the pseudo code, surely more understandable then my words.
class a:
def __init__(self):
self.value = 45
class b(a):
def __init__(self):
self.other_value = #this should be self.value from class a
You need call the parent constructor first
class B(A):
def __init__(self):
super().__init__()
print(self.value)
you should call the parent constructor:
class b(a):
def __init__(self):
a.__init__(self)
#now you can use it with self.value
I have some code that looks like this:
class Log(object):
#property
def log(self):
return self.log
class ExampleClass2(ExampleClass, Log):
class ExampleClass3(object):
#property
def log_value(self):
self.log.info('Hi!')
However I'm getting an error,
'ExampleClass3' object has not attribute 'log'
I'm guessing I need to add an __init__() method to DEF, and I've tried using
super(ExampleClass2.ExampleClass3, self).__init__()
but I'm still having problems accessing log. Any suggestions?
I believe to get your desired behavior, you need need to pass in an instance of ExampleClass2 when you create an instance of ExampleClass3.
class OuterClass:
def __init__(self, value):
self.value = value
class InnerClass:
def __init__(self, instance):
self.instance = instance
def inner_print_value(self):
print self.instance.value
def outer_print_value(self):
printer = OuterClass.InnerClass(self)
printer.inner_print_value()
OuterClass('Hi').outer_print_value() # 'Hi'
As noted in the comments, there is rarely a reason for this kind of structure. It would be easier to create InnerClass outside of the definition of OuterClass.
class OuterClass:
def __init__(self, value):
self.value = value
def outer_print_value(self):
printer = InnerClass(self)
printer.inner_print_value()
class InnerClass:
def __init__(self, instance):
self.instance = instance
def inner_print_value(self):
print self.instance.value
It seems like you're expecting the value of self to be augmented when creating an inner-class, but this is not the case. To do this, you'd want to use inheritance, and that doesn't require nested classes either.
Or does the attribute have to be defined outside of any class methods?
So my descriptor object is this. The IDN object already has some information about the UserNameField, so I want to use it.
class UserNameElement(basePageElement):
_testMethodName="UserNameElement Test method"
def __init__(self, IDN, PTF):
print "creating UserNameElement"
self.locator = IDN.UserNameField()
And here is my calling class. Where I want to instantiate the UserNameElement object
class LoginPageObject(basePageObject):
_testMethodName="LoginPageObject Test method"
print "creating LoginPageObject"
def __init__(self, BaseURL):
super(LoginPageObject, self).__init__()
self.username=UserNameElement(IDN=self.IDN, PTF=self.PTF)
It seems that the standard process would put the username= in in the general class definition, like this:
class LoginPageObject(basePageObject):
_testMethodName="LoginPageObject Test method"
username=UserNameElement()
print "creating LoginPageObject"
def __init__(self, BaseURL):
super(LoginPageObject, self).__init__()
But then I don't have the PTF and IDN that I define in the basePageObject class.
What can I do to make those available when the username attribute is created?
Thanks
I am afraid that will not be possible, as your attribute username will be resolved via normal attribute access see http://docs.python.org/howto/descriptor.html#invoking-descriptors
May be you can get away by overriding __getattribute__ and simulating what type.__getattribute__() does
class MyD(object):
def __init__(self, val):
self.val = val
def __get__(self, obj, objtype):
return self.val
def __set__(self, obj, val):
self.val = val
class C(object):
a = MyD(42)
def __init__(self):
self.d = MyD(42)
def __getattribute__(self, name):
attr = super(C, self).__getattribute__(name)
if hasattr(attr, '__get__'):
return attr.__get__(self, C)
return attr
c = C()
print c.d
print c.a
Output:
42
42
Since you probably won't need the username until the object has been instantiated, it's probably best to just make it a property and write a getter for it.