I have a superclass that has a lot of arguments. I want to create a subclass that shares all of those arguments and adds additional one or two arguments. To ommit double-coding, I used a method specified in Avoid specifying all arguments in a subclass :
class Container(Item):
def __init__(self,**kwargs):
try: self.is_locked=kwargs.pop('is_locked')
except KeyError: pass
super(Container, self).__init__(**kwargs)
def open(self):
print "aw ys"
However, when I then try to call an object of a Container class:
> some_container.open()
AttributeError: 'Item' object has no attribute 'open'
It appears as if the some_container is not a Container() but rather an Item() with a single variable is_locked added. What am I doing wrong?
edit: My Item definition:
class Item(object:
def __init__(self,istemplate,id,short,long,type,flags,value,damagerange,damagereductionrange,weight):
if istemplate==False:
self.__class__.instances.append(self)
self.istemplate=istemplate
(... many variables like that...)
self.id=id
self.randomizestuff()
if istemplate==True:
self.__class__.templates.append(copy.deepcopy(self))
Okay, after some research it turned out I was in fact referencing not a container in some_container.open() but rather a dynamic item created by a function that creates instances of items from the same template. That function would define new instances as Item(...) rather than Container(...) since it was created before introduction of any subclasses.
some_container=Container(...)
some_container.open()
The above would work from the beginning, hence paidhima couldn't replicate my error.
Related
I have a class
class A:
def sample_method():
I would like to decorate class A sample_method() and override the contents of sample_method()
class DecoratedA(A):
def sample_method():
The setup above resembles inheritance, but I need to keep the preexisting instance of class A when the decorated function is used.
a # preexisting instance of class A
decorated_a = DecoratedA(a)
decorated_a.functionInClassA() #functions in Class A called as usual with preexisting instance
decorated_a.sample_method() #should call the overwritten sample_method() defined in DecoratedA
What is the proper way to go about this?
There isn't a straightforward way to do what you're asking. Generally, after an instance has been created, it's too late to mess with the methods its class defines.
There are two options you have, as far as I see it. Either you create a wrapper or proxy object for your pre-existing instance, or you modify the instance to change its behavior.
A proxy defers most behavior to the object itself, while only adding (or overriding) some limited behavior of its own:
class Proxy:
def __init__(self, obj):
self.obj = obj
def overridden_method(self): # add your own limited behavior for a few things
do_stuff()
def __getattr__(self, name): # and hand everything else off to the other object
return getattr(self.obj, name)
__getattr__ isn't perfect here, it can only work for regular methods, not special __dunder__ methods that are often looked up directly in the class itself. If you want your proxy to match all possible behavior, you probably need to add things like __add__ and __getitem__, but that might not be necessary in your specific situation (it depends on what A does).
As for changing the behavior of the existing object, one approach is to write your subclass, and then change the existing object's class to be the subclass. This is a little sketchy, since you won't have ever initialized the object as the new class, but it might work if you're only modifying method behavior.
class ModifiedA(A):
def overridden_method(self): # do the override in a normal subclass
do_stuff()
def modify_obj(obj): # then change an existing object's type in place!
obj.__class__ = ModifiedA # this is not terribly safe, but it can work
You could also consider adding an instance variable that would shadow the method you want to override, rather than modifying __class__. Writing the function could be a little tricky, since it won't get bound to the object automatically when called (that only happens for functions that are attributes of a class, not attributes of an instance), but you could probably do the binding yourself (with partial or lambda if you need to access self.
First, why not just define it from the beginning, how you want it, instead of decorating it?
Second, why not decorate the method itself?
To answer the question:
You can reassign it
class A:
def sample_method(): ...
pass
A.sample_method = DecoratedA.sample_method;
but that affects every instance.
Another solution is to reassign the method for just one object.
import functools;
a.sample_method = functools.partial(DecoratedA.sample_method, a);
Another solution is to (temporarily) change the type of an existing object.
a = A();
a.__class__ = DecoratedA;
a.sample_method();
a.__class__ = A;
I have a class with a private constant _BAR = object().
In a child class, outside of a method (no access to self), I want to refer to _BAR.
Here is a contrived example:
class Foo:
_BAR = object()
def __init__(self, bar: object = _BAR):
...
class DFoo(Foo):
"""Child class where I want to access private class variable from parent."""
def __init__(self, baz: object = super()._BAR):
super().__init__(baz)
Unfortunately, this doesn't work. One gets an error: RuntimeError: super(): no arguments
Is there a way to use super outside of a method to get a parent class attribute?
The workaround is to use Foo._BAR, I am wondering though if one can use super to solve this problem.
Inside of DFoo, you cannot refer to Foo._BAR without referring to Foo. Python variables are searched in the local, enclosing, global and built-in scopes (and in this order, it is the so called LEGB rule) and _BAR is not present in any of them.
Let's ignore an explicit Foo._BAR.
Further, it gets inherited: DFoo._BAR will be looked up first in DFoo, and when not found, in Foo.
What other means are there to get the Foo reference? Foo is a base class of DFoo. Can we use this relationship? Yes and no. Yes at execution time and no at definition time.
The problem is when the DFoo is being defined, it does not exist yet. We have no start point to start following the inheritance chain. This rules out an indirect reference (DFoo -> Foo) in a def method(self, ....): line and in a class attribute _DBAR = _BAR.
It is possible to work around this limitation using a class decorator. Define the class and then modify it:
def deco(cls):
cls._BAR = cls.__mro__[1]._BAR * 2 # __mro__[0] is the class itself
return cls
class Foo:
_BAR = 10
#deco
class DFoo(Foo):
pass
print(Foo._BAR, DFoo._BAR) # 10 20
Similar effect can be achieved with a metaclass.
The last option to get a reference to Foo is at execution time. We have the object self, its type is DFoo, and its parent type is Foo and there exists the _BAR. The well known super() is a shortcut to get the parent.
I have assumed only one base class for simplicity. If there were several base classes, super() returns only one of them. The example class decorator does the same. To understand how several bases are sorted to a sequence, see how the MRO works (Method Resolution Order).
My final thought is that I could not think up a use-case where such access as in the question would be required.
Short answer: you can't !
I'm not going into much details about super class itself here. (I've written a pure Python implementation in this gist if you like to read.)
But now let's see how we can call super:
1- Without arguments:
From PEP 3135:
This PEP proposes syntactic sugar for use of the super type to
automatically construct instances of the super type binding to the
class that a method was defined in, and the instance (or class object
for classmethods) that the method is currently acting upon.
The new syntax:
super()
is equivalent to:
super(__class__, <firstarg>)
...and <firstarg> is the first parameter of the method
So this is not an option because you don't have access to the "instance".
(Body of the function/methods is not executed unless it gets called, so no problem if DFoo doesn't exist yet inside the method definition)
2- super(type, instance)
From documentation:
The zero argument form only works inside a class definition, as the
compiler fills in the necessary details to correctly retrieve the
class being defined, as well as accessing the current instance for
ordinary methods.
What were those necessary details mentioned above? A "type" and A "instance":
We can't pass neither "instance" nor "type" which is DFoo here. The first one is because it's not inside the method so we don't have access to instance(self). Second one is DFoo itself. By the time the body of the DFoo class is being executed there is no reference to DFoo, it doesn't exist yet. The body of the class is executed inside a namespace which is a dictionary. After that a new instance of type type which is here named DFoo is created using that populated dictionary and added to the global namespaces. That's what class keyword roughly does in its simple form.
3- super(type, type):
If the second argument is a type, issubclass(type2, type) must be
true
Same reason mentioned in above about accessing the DFoo.
4- super(type):
If the second argument is omitted, the super object returned is
unbound.
If you have an unbound super object you can't do lookup(unless for the super object's attributes itself). Remember super() object is a descriptor. You can turn an unbound object to a bound object by calling __get__ and passing the instance:
class A:
a = 1
class B(A):
pass
class C(B):
sup = super(B)
try:
sup.a
except AttributeError as e:
print(e) # 'super' object has no attribute 'a'
obj = C()
print(obj.sup.a) # 1
obj.sup automatically calls the __get__.
And again same reason about accessing DFoo type mentioned above, nothing changed. Just added for records. These are the ways how we can call super.
I am trying to make a class within a module, import that module file in my controller, and then reference the class that is defined within that module, but I keep getting a message that reads NameError("name 'self' is not defined")
Here is my code in my created module:
from gluon import *
class device_info(object):
self.info = {}
def __init__(self, info):
self.info = info
return
def setInfo(info):
self.info = info
return
def getInfo():
return self.info`
Does anyone know what causes this and how it can be resolved? I was under the impression that user-defined classes were supported in web2py.
As stated, just move self.info = {} into __init__().
__init__() is essentially a constructor that you are familiar with from java. It initializes an instance object of that class when called. I haven't used Java in some time, but I don't think you should be be declaring class variables outside of your constructor there either.
self is an argument that all methods within a class in python must receive as their first argument. So your getters and setters are also not going to work if you try them; they must be:
def setInfo(self, info) and def getInfo(self)
When you create an object, like this:
device1 = device_info()
it calls __init()__, passing device1 as self. Then, whenever you use that object, such as
device1.setInfo(newInfo), you can think of the method in the class' context being called as setInfo(device1, newInfo), since device1 is self, or the current instance of the device_info object in use.
You also don't need the object argument at the class definition. What do you expect that to do?
Edit: Actually, don't move self.info = {} into __init__(), just get rid of it. You already have self.info = info in __init__(). You don't need to initialize variables like that in Python like you do in Java. Creating an empty dict and then setting it to another dict without any use is redundant.
I'm trying to modify class attribute by reference to object in __init__ method and then use it in another method. Sadly the following code sample doesn't work as expected...
CODE
class Translator:
#list of attributes
parser=None
def __init__(self):
parser = Parser_class() ...
#some other commands
def Translate(self):
something=self.parser.GenerateHead() ...
#more commands
COMPILE ERR
AttributeError: 'NoneType' object has no attribute 'GenerateHead'
I know that I can give it to the Translate method as argument, I'm just curious why this statement within Python doesn't work.
You're doing your instance attributes wrong.
First off, you don't need to declare your attributes ahead of time. Putting parser = None at the top level of the class creates a class variable named parser, which I don't think is what you want. Usually in Python you can add new instance attributes at any time by a simple assignment: instance.attr = "whatever".
Second, when you want to do an instance assignment from within a method, you need to use self to refer to the instance. If you leave off self, you'll be assigning to a local variable inside your function, not to an instance or class variable. Actually, the specific name self isn't necessary, but you do need to use the first argument to the method (and it's probably not a good idea to break the convention of naming that self).
So, to fix your code, do this:
class Translator:
# don't declare variables at class level (unless you actually want class variables)
def __init__(self):
self.parser = Parser_class() # use self to assign an instance attribute
def Translate(self):
something = self.parser.GenerateHead() # this should now work
While researching about python class attribute and instance attribute, I came to know that it's not possible to create object attribute outside object methods (or may be class method). Like code below will generate an "NameError" in python.
class test(object):
def __init__(self):
self.lst = []
self.str = 'xyz'
Why python doesn't allow this? I'm not questioning language creator's decision, but any reason behind this. Like, is it technically incorrect or any other disadvantage of this behavior.
You are defining a class, so there is no instance to point to outside methods. Drop the `self:
class test(object):
def __init__(self):
self.lst = []
str = 'xyz'
self points to the instance, not the class. You either need to create an instance and assign directly to attributes (test().str = 'xyz') or you need to be inside a method (when self can actually refer to an instance).
self is not a special name in python, you could use \
class test(object):
def __init__(foo):
foo.lst = []
If you want. Every method of a class gets the instance explicitly passed to it as the first parameter, you can call it whatever you want. Trying to access a parameter outside the scope of the method obviously won't work.