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
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?
So basically my problem seems like this.
class A():
def func(self):
return 3
class B():
def func(self):
return 4
class AA(A):
def func(self):
return super(AA, self).func
class BB(B):
def func(self):
return super(BB, self).func
The func function is doing some work and one of the things it does is getting some attribute(or running method or whatever) from it's parent class.
Since func originally does the same logic at both cases (except that only parent class changes) I'd like to do this with decorators.
Is it possible? if so how to do it? Do I have somehow to pass parent-class as a argument?
I'll be very grateful for answers it's been bothering me for a while now.
There is no need to use super to access data attributes of a parent class.
Neither does a class need a parent in order for access to data attributes to work.
You can use a mixin to do the job:
# A and B stay the same - they still have a c attribute
class A():
c = 3
class B():
c = 4 # I've changed B to make it clear below
#Instead have a mixin which defines func()
class Mixin:
def func(self):
# func has its behaviour here
return self.c
class AA(Mixin, A):
pass
class BB(Mixin, B):
pass
a = AA()
b = BB()
print(a.func())
print(b.func())
Output:
3
4
You could do it with a single class decorator by defining a generic method inside of it that does what you want, and then adding it to the class being decorated. Here's what I mean:
def my_decorator(cls):
def call_super_func(self):
return super(type(self), self).func()
setattr(cls, 'call_super_func', call_super_func)
return cls
class A():
def func(self):
print('in A.func')
return 3
class B():
def func(self):
print('in B.func')
return 4
#my_decorator
class AA(A):
def func(self):
print('in AA.func')
return self.call_super_func()
#my_decorator
class BB(B):
def func(self):
print('in BB.func')
return self.call_super_func()
aa = AA()
aa.func()
bb = BB()
bb.func()
Output:
in AA.func
in A.func
in BB.func
in B.func
Of course you could eliminate the need to do this by just defining baseclass for A and B that has a call_super_func() method in it that they would then both inherit.
Sorry, may be this is silly question, but its very confusing to me. Let's suppose we have the following classes:
class A():
def say(self):
print("A")
class B(A):
def say(self):
print("B")
class C(B):
def say(self,*args, **kwargs):
return super(C, self).say(*args, **kwargs)
I am accessing parent method in child, and it prints B, but I want to access method from class A as we are getting access from class B.
I know we can add super in class B, but I don't want to modify class B. so is there any option to get method from A directly in class C?
You can by calling A.say(self) like this:
class A():
def say(self):
print("A")
class B(A):
def say(self):
print("B")
class C(B):
def say(self):
A.say(self)
B.say(self)
print("C")
Then to test it out from a terminal:
>>> a = A()
>>> a.say()
A
>>> b = B()
>>> b.say()
B
>>> c = C()
>>> c.say()
A
B
C
Note: I dropped the args and kwargs because the A and B classes didn't use those arguments. If you wanted to make say take those all the way up though simply call A.say(self, *args, **kwargs) and if A.say returns something you can return it too
In python, is there a way to prevent adding new class variables after defining the object?
For example:
class foo:
def __init__(self):
self.a = 1
self.b = 2
self.c = 3
bar = foo()
try:
bar.d = 4
except Exception, e:
print "I want this to always print"
Alternatively, is there a way to count the number of variables in an object?
class foo:
def __init__(self):
self.a = 1
self.b = 2
self.c = 3
def count(self):
...
bar = foo()
if bar.count() == 3:
print "I want this to always print"
The only way I thought of doing this was using a dictionary or list:
class foo:
def __int__(self):
self.dict = {'foo':1, 'bar':2}
self.len = 2
def chk():
return self.len == len(self.list)
However, doing this feels rather cumbersome for python. (obj.dict['foo']). I'd prefer just obj.foo if possible.
I want to have this so that I never accidentally declare a variable when I mean to change an existing one.
f = foo()
f.somename = 3
...
f.simename = 4 #this is a typo
if f.somename == 3:
solve_everything()
I suggest using __setattr__ to avoid the oddities of __slots__.
You always have to be careful when messing with __setattr__, since it takes care of setting all instance attributes, including those you set in __init__. Therefore it has to have some way of knowing when to allow the setting of an attribute, and when to deny it. In this solution I've designated a special attribute that controls whether new attributes are allowed or not:
class A(object):
def __init__(self):
self.a = 1
self.b = 2
self.c = 3
self.freeze = True
def __setattr__(self, attr, value):
if getattr(self, "freeze", False) and not hasattr(self, attr):
raise AttributeError("You shall not set attributes!")
super(A, self).__setattr__(attr, value)
Testing:
a = A()
try:
a.d = 89
except AttributeError:
print "It works!"
else:
print "It doesn't work."
a.c = 42
print a.a
print a.c
a.freeze = False
a.d = 28
a.freeze = True
print a.d
Result:
It works!
1
42
28
Also see gnibblers answer that wraps this concept neatly up in a class decorator, so it doesn't clutter up the class definition and can be reused in several classes without duplicating code.
EDIT:
Coming back to this answer a year later, I realize a context manager might solve this problem even better. Here's a modified version of gnibbler's class decorator:
from contextlib import contextmanager
#contextmanager
def declare_attributes(self):
self._allow_declarations = True
try:
yield
finally:
self._allow_declarations = False
def restrict_attributes(cls):
cls.declare_attributes = declare_attributes
def _setattr(self, attr, value):
disallow_declarations = not getattr(self, "_allow_declarations", False)
if disallow_declarations and attr != "_allow_declarations":
if not hasattr(self, attr):
raise AttributeError("You shall not set attributes!")
super(cls, self).__setattr__(attr, value)
cls.__setattr__ = _setattr
return cls
And here's how to use it:
#restrict_attributes
class A(object):
def __init__(self):
with self.declare_attributes():
self.a = 1
self.b = 2
self.c = 3
So whenever you want to set new attributes, just use the with statement as above. It can also be done from outside the instance:
a = A()
try:
a.d = 89
except AttributeError:
print "It works!"
else:
print "It doesn't work."
a.c = 42
print a.a
print a.c
with a.declare_attributes():
a.d = 28
print a.d
In python, is there a way to prevent adding new class variables after defining the object?
Yes. __slots__. But do carefully read the notes.
How about a class decorator based on lazyr's answer
def freeze(cls):
_init = cls.__init__
def init(self, *args, **kw):
_init(self, *args, **kw)
self.freeze = True
cls.__init__ = init
def _setattr(self, attr, value):
if getattr(self, "freeze", None) and (attr=="freeze" or not hasattr(self, attr)):
raise AttributeError("You shall not set attributes!")
super(cls, self).__setattr__(attr, value)
cls.__setattr__ = _setattr
return cls
#freeze
class foo(object):
def __init__(self):
self.a = 1
self.b = 2
self.c = 3
bar = foo()
try:
bar.d = 4
except Exception, e:
print "I want this to always print"
Preventing adding new attibutes using __slots__ class attribute:
class foo(object):
__slots__ = ['a', 'b', 'c']
def __init__(self):
self.a = 1
self.b = 2
self.c = 3
bar = foo()
try:
bar.d = 4
except Exception as e:
print(e,"I want this to always print")
Counting attributes:
print(len([attr for attr in dir(bar) if attr[0] != '_' ]))
use this to count no.of attributes of an instance:
>>> class foo:
def __init__(self):
self.a = 1
self.b = 2
self.c = 3
>>> bar=foo()
>>> bar.__dict__
{'a': 1, 'c': 3, 'b': 2}
>>> len(bar.__dict__) #returns no. of attributes of bar
3
Do you mean new class variables or new instance variables? The latter looks like what you mean and is much easier to do.
Per Ignacio Vazquez-Abrams's answer, __slots__ is probably what you want. Just do __slots__ = ('a', 'b', 'c') inside of your class and that will prevent any other attributes from being created. Note that this only applies to instances of your class -- class-level attributes can still be set, and subclasses can add whatever attributes they please. And he is right -- there are some oddities, so read the linked documentation before you start sprinkling slots everywhere.
If you aren't using slots, return len(vars(self)) works as a body for your suggested count method.
As an alternative to slots, you could define a __setattr__ that rejects any attribute not on a "known good" list, or to reject any new attributes after a frozen attribute is set to True at the end of __init__, etc. This is harder to get right, but more flexible.
If you actually want your instances to be completely read-only after initialization, and you are using a recent version of Python, consider defining a namedtuple or subclass thereof. Tuple subclasses also have some limitations though; if you need to go this route I can expand on it, but I'd stick with slots unless you have a reason to do otherwise.
Suppose you now want your class to have a fixed set of both mutable and immutable attributes? I've hacked gnibbler's answer to make class attributes immutable after init:
def frozenclass(cls):
""" Modify a class to permit no new attributes after instantiation.
Class attributes are immutable after init.
The passed class must have a superclass (e.g., inherit from 'object').
"""
_init = cls.__init__
def init(self, *args, **kw):
_init(self, *args, **kw)
self.freeze = True
cls.__init__ = init
def _setattr(self, attr, value):
if getattr(self, "freeze", None):
if attr=="freeze" or not hasattr(self, attr):
raise AttributeError("You shall not create attributes!")
if hasattr(type(self), attr):
raise AttributeError("You shall not modify immutable attributes!")
super(cls, self).__setattr__(attr, value)
cls.__setattr__ = _setattr
return cls
And an example:
#frozenclass
class myClass(object):
""" A demo class."""
# The following are immutable after init:
a = None
b = None
c = None
def __init__(self, a, b, c, d=None, e=None, f=None):
# Set the immutable attributes (just this once, only during init)
self.a = a
self.b = b
self.c = c
# Create and set the mutable attributes (modifyable after init)
self.d = d
self.e = e
self.f = f