I have a class and a sub-class, I'd like to pass the whole of the self of the class to the sub-class. I can pass self over to the new class explicitly easily enough, e.g.
class foo:
def __init__(self, a, b):
self.a = a
self.b = b
self.c = 'foo'
def foo_method(self):
print('a foo method')
class bar(foo):
def __init__(self, foo_object):
self.a = foo_object.a
self.b = foo_object.b
self.c = foo_object.c
def bar_method(self):
print('a bar method')
foo_object = foo(a = 'a', b = 'b')
bar_object = bar(foo_object)
bar_object.a
Is there a more succinct way to pass these over? Something like:
class bar(foo):
def __init__(self, foo_object):
self = self.foo_object
Update:
Thanks https://stackoverflow.com/users/10104112/bastien-antoine, the following solution worked:
class bar(foo):
def __init__(self, foo_object):
self.__dict__ = foo_object.__dict__.copy()
def bar_method(self):
print('a bar method with ' + str(self.c))
Have you tried the copy builtins library?
Otherwise I think you can easily implement your own .copy() method that would copy the values from the old object __dict__ to the new one. Something like this:
class MyObject:
a = None
def set_default_values(self):
self.a = 1
def copy(self, old):
if type(self) == type(old):
self.__dict__ = old.__dict__.copy()
else:
raise TypeError('Wrong type')
if __name__ == "__main__":
obj_1 = MyObject()
print(obj_1.a)
obj_1.set_default_values()
print(obj_1.a)
obj_2 = MyObject()
print(obj_2.a)
obj_2.copy(obj_1)
print(obj_2.a)
Note that I've added a type checking to be sure that you copy attributes that would exist otherwise, but I think simply self.__dict__ = old.__dict__.copy() would work fine, thought you might end up with attributes you might not suppose to have in the new object.
Hope this helps!
I think that you can do that with
class bar(foo):
def __init__(self):
super(bar, self).__init__()
with this code, you ran the init function for the subclass
Related
The get_calling_class function must pass the following tests by returning the class of the method that called the A.f method:
class A:
def f(self): return get_calling_class()
class B(A):
def g(self): return self.f()
class C(B):
def h(self): return self.f()
c = C()
assert c.g() == B
assert c.h() == C
Walking the stack should give the answer.
The answer should ideally be, in the caller's stack frame.
The problem is, the stack frames only record the function
names (like so: 'f', 'g', 'h', etc.) Any information about
classes is lost. Trying to reverse-engineer the lost info,
by navigating the class hierarchy (in parallel with the
stack frame), did not get me very far, and got complicated.
So, here is a different approach:
Inject the class info into the stack frame
(e.g. with local variables),
and read that, from the called function.
import inspect
class A:
def f(self):
frame = inspect.currentframe()
callerFrame = frame.f_back
callerLocals = callerFrame.f_locals
return callerLocals['cls']
class B(A):
def g(self):
cls = B
return self.f()
def f(self):
cls = B
return super().f()
class C(B):
def h(self):
cls = C
return super(B, self).f()
def f(self):
cls = C
return super().f()
c = C()
assert c.h() == C
assert c.g() == B
assert c.f() == B
Related:
get-fully-qualified-method-name-from-inspect-stack
Without modifying the definition of subclasses:
Added an "external" decorator, to wrap class methods.
(At least as a temporary solution.)
import inspect
class Injector:
def __init__(self, nameStr, valueStr):
self.nameStr = nameStr
self.valueStr = valueStr
# Should inject directly in f's local scope / stack frame.
# As is, it just adds another stack frame on top of f.
def injectInLocals(self, f):
def decorate(*args, **kwargs):
exec(f'{self.nameStr} = {self.valueStr}')
return f(*args, **kwargs)
return decorate
class A:
def f(self):
frame = inspect.currentframe()
callerDecoratorFrame = frame.f_back.f_back # Note:twice
callerDecoratorLocals = callerDecoratorFrame.f_locals
return callerDecoratorLocals['cls']
class B(A):
def g(self): return self.f()
def f(self): return super().f()
class C(B):
def h(self): return super(B, self).f()
def f(self): return super().f()
bInjector = Injector('cls', B.__name__)
B.g = bInjector.injectInLocals(B.g)
B.f = bInjector.injectInLocals(B.f)
cInjector = Injector('cls', C.__name__)
C.h = cInjector.injectInLocals(C.h)
C.f = cInjector.injectInLocals(C.f)
c = C()
assert c.h() == C
assert c.g() == B
assert c.f() == B
I found this link very interesting
(but didn't take advantage of metaclasses here):
what-are-metaclasses-in-python
Maybe someone could even replace the function definitions*,
with functions whose code is a duplicate of the original;
but with added locals/information, directly in their scope.
*
Maybe after the class definitions have completed;
maybe during class creation (using a metaclass).
Consider the code below:
class A(object):
attr = None
def __init__(self):
assert A.attr is not None
class B(object):
def __init__(self, attr):
A.attr = attr
class C(A):
def __init__(self):
super().__init__()
class D(A):
def __init__(self):
super().__init__()
nested_classes = {cls.__name__: cls for cls in {C, D}}
Above doesn't seem to work as I intend because:
>>> first_class = B("first")
>>> first_sub_class = first_class.C()
>>> first_sub_class.attr
'first'
>>> second_class = B("second")
>>> second_sub_class = second_class.C()
>>> second_sub_class.attr
'second'
>>> first_sub_class.attr
'second'
Is there a way to have first_sub_class.attr be first while having second_sub_class.attr be second? Possibly by having a metaclass whose scope is within B?
A few points:
I don't want to pass attr around, I want to set it while B is being initialized.
I don't want to circumnavigate the point above by using partial, since it breaks the rest of the code relying on __name__ or __qualname__ or alike.
I want to keep faithful to the current structure as much as possible.
To solve this problem just add the line self.attr = self.attr inside the __init__ function of A. Since you don't want to change the attributes of A, you will have to make the following changes:
class A(object):
attr = None
def __init__(self):
assert self.attr is not None # Don't refer to A, but to self to get the correct value
self.attr = self.attr
class B(object):
def __init__(self, attr):
self.attr = attr # Don't edit A, just save the value in own instance
def __getattribute__(self, item): # completely added, does some magic to ensure class.attr is set correctly
if item in B.nested_classes:
c = B.nested_classes[item]
c.attr = self.attr
return c
return super().__getattribute__(item)
class C(A):
def __init__(self):
super().__init__()
class D(A):
def __init__(self):
super().__init__()
nested_classes = {cls.__name__: cls for cls in {C, D}}
first_class = B("first")
first_sub_class = first_class.C()
print(first_sub_class.attr)
second_class = B("second")
second_sub_class = second_class.C()
print(second_sub_class.attr)
print(first_sub_class.attr)
You're insanely overcomplicating this:
class A:
def __init__(self, attr):
self.attr = attr
class C(A):
pass
class D(A):
pass
class B:
def __init__(self, attr):
self.attr = attr
def C(self):
return C(self.attr)
def D(self):
return D(self.attr)
Behaves exactly as desired:
>>> first_class = B("first")
>>> first_sub_class = first_class.C()
>>> first_sub_class.attr
'first'
>>> second_class = B("second")
>>> second_sub_class = second_class.C()
>>> second_sub_class.attr
'second'
>>> first_sub_class.attr
'first'
If I do something like this:
class MyClass(object):
def __init__(self, a=MyClass.f):
self.a = a
#classmethod
def f():
print 'tump drump'
I get the following error:
NameError: name 'MyClass' is not defined
Obviously, I could do this:
class MyClass(object):
def __init__(self, a=None):
if a is None:
self.a = MyClass.f
else:
self.a = a
But is there a more elegant way to use a classmethod as default argument of a class method?
No, there isn't, because the functions are created before the class object is. There is not class to reference here, and the use of a sentinel (like None) is the correct way to do this.
Note that there is no need to use an else suite if you assign to a rather than self.a in the if suite:
class MyClass(object):
def __init__(self, a=None):
if a is None:
a = MyClass.f
self.a = a
or you could use a conditional expression:
class MyClass(object):
def __init__(self, a=None):
self.a = MyClass.f if a is None else a
or even:
class MyClass(object):
def __init__(self, a=None):
self.a = a or MyClass.f
if all you need to support is truthy objects (function objects always are 'true' in a boolean context, for example).
I have a Class B inheriting Class A with a class attribute cls_attr.
And I would like to set dynamically cls_attr in class B.
Something like that:
class A():
cls_attr= 'value'
class B(A):
def get_cls_val(self):
if xxx:
return cls_attr = 'this_value'
return cls_attr = 'that_value'
cls_attr = get_cls_val()
I tried several things. I know i might not be looking in the right place but i am out of solutions.
EDIT: Classes are django admin classes
Thanks.
class attributes can be read on the class or an instance, but you can only set them on the class (trying to set them on an instance will only create an instance attribute that will shadow the class attribute).
If the condition is known at import time, you can just test it in the class body:
xxx = True
class A(object):
cls_attr = 'value'
class B(A):
if xxx:
cls_attr = 'this_value'
else
cls_attr = 'that_value'
Now if you want to change it during the program's execution, you either have to use a classmethod:
class B(A):
#classmethod
def set_cls_attr(cls, xxx):
if xxx:
cls.cls_attr = 'this_value'
else:
cls.cls_attr = 'that_value'
or if you need to access your instance during the test:
class B(A):
def set_cls_attr(self, xxx):
cls = type(self)
if xxx:
cls.cls_attr = 'this_value'
else:
cls.cls_attr = 'that_value'
What about using classmethod and polymorphically overriding it in subclass?
class A:
#classmethod
def cls_attr(cls):
return 'value'
class B(A):
#classmethod
def cls_attr(cls):
if cond():
return 'this'
else:
return 'that'
assert A.cls_attr() == 'value'
cond = lambda: True
assert B.cls_attr() == 'this'
cond = lambda: False
assert B.cls_attr() == 'that'
The easiest solution for me is with property decorator:
class B:
#property
def attr_name(self):
""" do your stuff to define attr_name dynamically """
return attr_name
This seems to do what you want:
>>> class B(A):
#classmethod
def set_cls_val(cls, x):
if x == 1:
cls.cls_attr = "new"
>>> c = B()
>>> c.cls_attr
'value'
>>> c.set_cls_val(B, 1)
>>> c.cls_attr
'new'
>>> B.cls_attr
'new'
Just set it within the function.
EDIT: Updated to set the class attribute and not the instance attribute, thanks #bruno-desthuilliers.
EDIT: Updates once again, thanks #bruno-desthuilliers. I should think my answers through more clearly. But what you want is answered below.
class A()
att = B()
class B()
...
a = A()
b = B()
a.att = b
How can b get reference of a ? I need to get an attribute of a here.
Thanks!
You can make a generic "Reference()" class, that keep any reference of itself in an attributes dictionnary.
class Reference(object):
def __init__(self):
self.references = {}
def __setattr__(self, key, value):
if hasattr(self, 'references'):
if isinstance(value, Reference):
if not key in value.references:
value.references[key] = []
value.references[key].append(self)
elif value is None and hasattr(self, key):
old = getattr(self, key).references
if key in old and self in old[key]:
old[key].remove(self)
super(Reference, self).__setattr__(key, value)
And then, create your classes :
class A(Reference):
def __init__(self):
super(A, self).__init__()
self.att = None
class B(Reference):
def __init__(self):
super(B, self).__init__()
self.att = None
And use it :
a = A()
b = B()
print 'A references', a.references
print 'B references', b.references
# A references {}
# B references {}
a.att = b
print 'A references', a.references
print 'B references', b.references
# A references {}
# B references {'att': [<__main__.A object at 0x7f731c8fc910>]}
At the end, you'll have back reference to all Reference class from any properties
Easiest way would be to just add an extra function parameter to the method in B that needs A, and pass it through when called. Or, just make B's init take an A as argument, and change the bit in A's init to be att = B(self)
class A(object):
def __init__(self):
self.att = B(self)
class B(object):
def __init__(self, a):
self.a = a
a = A()
a.att.a is a
Or another way,
class A(object):
def __init__(self, b):
b.a = self
self.att = b
class B(object):
pass
a = A(B())
a.att.a is a
This code doesn't make a lot of sense... but if I correctly understand your question...
class A(object):
pass #or whatever you like
class B(object):
def __init__(self, ref): #accept one argument
self.ref = ref
a = A()
b = B(a) #pass `a` as that argument
a.att = b
Might be one answer.
class A(object):
def __init__(self):
self._att=None
#property
def att(self):
return self._att
#att.setter
def att(self, value):
self._att = value
value.parent = self
class B(object):
pass
a = A()
b = B()
a.att = b
print b.parent