If i have this:
class One(object):
def __init__(self, name):
self.name = name
I want to use One but altering the name name and relace it by other
The solution I supposed is inheriting:
class Two(One):
def __init__(self, other):
super(Two, self).__init__(other)
The idea is : How to delete or change the variable names that appears in __init__ ?
There is no relation at all between the name of the parameter passed to __init__ and the name of the instance variable that might eventuality be initialized by that argument. This is only a matter of convention than both are called the same.
Both code fragments below will perform exactly the same:
class One(object):
def __init__(self, name):
self.name = name
class One(object):
def __init__(self, xyz):
self.name = xyz
As about renaming an instance variable, you might do something like that, but this is (very) bad style and has (great) chances to break something in (the base class and/or in any client code that expects a proper One instance):
class Two(One):
def __init__(self, other):
super(Two, self).__init__(other)
self.other = self.name # <- no, seriously,
del self.name # <- don't do that !!!
You can't do what you want, not if you are calling One.__init__ from Two.__init__.
If you want to alter what attributes are set, simply don't call One.__init__() here. Set your own attributes instead:
class One(object):
def __init__(self, name):
self.name = name
class Two(One):
def __init__(self, other):
self.other = other
Now self.name will never be set. This most likely will break the rest of functionality in One, something you probably don't want to do. The rest of the methods in that class are likely to rely on certain attributes having been set.
In OOP terms, if Two is not a special kind of One object, don't inherit from One. If Two is a kind of One object, don't try to make it into something else.
Related
This may have been answered somewhere else, but I was wondering if there was any way to remove an attribute/method decorated with #property in a subclass.
Example:
from datetime import datetime
class A():
def __init__(self, num):
self._num = num
#property
def id(self):
return self._num * datetime.now().timestamp()
class B(A):
def __init__(self, id, num):
super().__init__(num)
self.id = id
The above code does not run if you attempt to create an instance of class B. AttributeError: can't set attribute
The base class uses a property because it needs to evaluate its ID on the fly, while my sub class is able to know its ID when it is created. The id attribute is accessed OFTEN, and I am seeing a significant performance hit because I have to use a property to serve this attribute, instead of just accessing it directly. (From what I have read, properties increase time-to-access by 5x). My application is currently spending around 10% of runtime getting this property.
Is there any way I can short-circuit the property in a sub class?
I'm going to go through several possibilities here. Some of them do what you literally asked. Some of them don't, but they may be better options anyway.
First, your example base class changes the value of obj.id on every access due to the passage of time. That's really bizarre and doesn't seem like a useful concept of "ID". If your real use case has a stable obj.id return value, then you can cache it to avoid the expense of recomputation:
def __init__(self):
...
self._id = None
#property
def id(self):
if self._id is not None:
return self._id
retval = self._id = expensive_computation()
return retval
This may mitigate the expense of the property. If you need more mitigation, look for places where you access id repeatedly, and instead, access it once and save it in a variable. Local variable lookup outperforms attribute access no matter how the attribute is implemented. (Of course, if you actually do have weird time-variant IDs, then this sort of refactoring may not be valid.)
Second, you can't override a property with a "regular" attribute, but you can create your own version of property that can be overridden this way. Your property blocks attribute setting, and takes priority over "regular" attributes even if you force an entry into the instance __dict__, because property has a __set__ method (even if you don't write a setter). Writing your own descriptor without a __set__ would allow overriding. You could do it with a generic LowPriorityProperty:
class LowPriorityProperty(object):
"""
Like #property, but no __set__ or __delete__, and does not take priority
over the instance __dict__.
"""
def __init__(self, fget):
self.fget = fget
def __get__(self, instance, owner=None):
if instance is None:
return self
return self.fget(instance)
class Foo(object):
...
#LowPriorityProperty
def id(self):
...
class Bar(Foo):
def __init__(self):
super(Bar, self).__init__()
self.id = whatever
...
Or with a role-specific descriptor class:
class IDDescriptor(object):
def __get__(self, instance, owner=None):
if instance is None:
return self
# Remember, self is the descriptor. instance is the object you're
# trying to compute the id attribute of.
return whatever(instance)
class Foo(object):
id = IDDescriptor()
...
class Bar(Foo):
def __init__(self):
super(Bar, self).__init__()
self.id = whatever
...
The role-specific descriptor performs better than the generic LowPriorityProperty, but both perform worse than property due to implementing more logic in Python instead of C.
Finally, you can't override a property with a "regular" attribute, but you can override it with another descriptor, such as another property, or such as the descriptors created for __slots__. If you're really, really pressed for performance, __slots__ is probably more performant than any descriptor you could implement manually, but the interaction between __slots__ and the property is weird and obscure and you'll probably want to leave a comment explaining what you're doing.
class Foo(object):
#property
def id(self):
...
class Bar(Foo):
__slots__ = ('id',)
def __init__(self):
super(Bar, self).__init__()
self.id = whatever
...
add a class C as common ancestor, without id. inherit A and B from it and implement id there as needed. Python wont care that id doesn’t exist on C.
refactor non-id code/attributes from A to C.
Suitability depends on whether OP controls class hierarchy and instantiation mechanisms.
I also found a workaround to get it working as is:
from datetime import datetime
class A():
def __init__(self, num):
self._num = num
#property
def id(self):
return self._num * datetime.now().timestamp()
class B(A):
#this fixes the problem
id = None
def __init__(self, id, num):
super().__init__(num)
self.id = id
b = B("id", 3)
print(vars(b))
This will output:
{'_num': 3, 'id': 'id'}
The trick is id = None on class B. Basically, Python's attribute/method lookup mechanism will stop at the first class with id as an attribute in the MRO. With id = None on class B, the lookup stops there and it never gets as far as that pesky #property on A.
If I comment it back out, as per the OP:
self.id = id
AttributeError: can't set attribute
I'm trying to call a function from a Class thats name will change depending on what type of enemy is being instantiated. How can I accomplish this?
My attempt was this: AssignClass.[self.Class](self)
but obviously that sintax makes no sense
class Creature:
def __init__(self, Name, Class):
self.Name = Name
self.Class = Class
# Using a variable function call to remove
# the need for a ton of 'if' statements
AssignClass.[self.Class](self)
# Basically automate doing this:
if self.Class = "Orc":
AssignClass.Orc(self)
elif self.Class = "Wizard"
AssignClass.Wizard(self)
class AssignClass:
def Orc(Creature):
Creature.Class='Orc'
Creature.Health=100
Creature.Mana=0
def Wizard(Creature):
Creature.Class='Wizard'
Creature.Health=75
Creature.Mana=200
Evil_Wizard = Creature("Evil Wizard", "Wizard")
You can retrieve class methods using getattr() and then just pass your Creature instance, e.g.:
class Creature:
def __init__(self, Name, Class):
self.Name = Name
self.Class = Class
getattr(AssignClass, Class)(self)
Btw. this is everything but a recommended style for Python classes, the least of which is that you're shadowing the Creature class in your AssignClass (which shouldn't be a class in the first place). I'd recommend you to read the PEP 8 -- Style Guide for Python Code thoroughly.
Played around a little more and found that I can use eval for this. (Safe as no user input can be added here)
class Creature:
def __init__(self, Name, Class):
self.Name = Name
self.Class = Class
eval('AssignClass.'+Class)(self)
Simple, silly question.
But say I had
class Stuff:
def __init__(self, name):
self.name = name:
def get_name(self):
print(name)
new_name = Stuff(name = "Richard")
new_name.get_name()
Would this work? Would get_name be able to access the name attribute and print it out?
I can't get this code to work...
There are a few things that you need to change but this works:
class Stuff:
def __init__(self, name):
self.name = name
def get_name(self):
print(self.name)
new_name = Stuff(name = "Richard")
new_name.get_name()
Besides a few syntax errors (class needs to be lowercase and some missing :) the main thing you were missing was accessing name by means of the self identifier. Since name is defined on the class you need to access it via self.
In most Python examples, when super is used to call a parent class's constructors, it appears at the top.
Is it bad form to have it at the bottom of an init method?
In the examples below, super is at the bottom of A's constructor, but at the top of B's constructor.
class A:
def __init__(self):
# Do some stuff
b = result_of_complex_operation()
super(A, self).__init__(b)
class B:
def __init__(self):
super(A, self).__init__(b)
# Do some stuff
This totally depends on the use case. Consider this.
class Foo():
def __init__(self):
print(self.name)
#property
def name(self):
return self.__class__.__name__
class Bar(Foo):
def __init__(self, name):
self.name = name
super().__init__()
#property
def name(self):
return self.__name
#name.setter
def name(self, name):
self.__name = name
If you'd invoke super() before setting self.name within Bar.__init__ you'd get an AttributeError because the required name has not yet been set.
Is it bad form to have it at the bottom of an init method?
You're asking the wrong question. Regardless of whether it's bad from or not, there are valid use cases for moving the superclass initialization to the bottom of a sub-class's constructor. Where to put the call to the superclass's constructor entirely depends on the implementation of the superclass's constructor.
For example, suppose you have a superclass. When constructing the superclass, you want to give an attribute a certain value depending on an attribute of the subclasses:
class Superclass:
def __init__(self):
if self.subclass_attr:
self.attr = 1
else:
self.attr = 2
As you can see from above, we expect the subclasses to have the attribute subclass_attr. So what does this mean? We can't initialize Supperclass until we've given the subclasses the subclass_attr attribute.
Thus, we have to defer calling the superclass's constructor until we initialize subclass_attr. In other words, the call to super will have to be put at the bottom of a subclasses constructor:
class Subclass(Superclass):
def __init__(self):
self.subclass_attr = True
super(Superclass, self).__init__()
In the end, the choice of where to put super should not be based upon some style, but on what's necessary.
I have some functionality wrapped up in a Python class (classa). classa inherits from another class supera.
I want exactly the same functionality as classa except that I want to inherit from superb.
I could just copy the class classa to a new class classb and then change the superclass for classb but obviously this very tacky, a maintenance headache and and I'm quite sure there's much better way - can anyone tell me what it is ?
EDIT: Thanks for answers so far. I should have said initially the my classa invokes super in its methods in order to invoke supera methods. It seems that this has some significance when looking at mixins as an option
This can be done with Python's multiple inheritance if none of the methods need to invoke super().
class Dog(object):
name = "Spot"
class Cat(object):
name = "Whiskers"
class SpeakingAnimalMixin(object):
def speak(self):
print "My name is", self.name, "and I can speak!"
class SpeakingDog(SpeakingAnimalMixin, Dog):
pass
class SpeakingCat(SpeakingAnimalMixin, Cat):
pass
SpeakingDog().speak()
My name is Spot and I can speak!
If you do need to invoke super() from a method then you need to create the class dynamically. This works fine, but the generated class's name will be less helpful and IDEs and other static analysis tools may be less useful.
You can create the class using a function, passing the superclass as an argument:
def make_speaking_animal_class(SpeechlessAnimal):
class SpeakingAnimal(SpeechlessAnimal):
def get_name(self):
return "Speaking " + super(SpeakingAnimal, self).get_name()
def speak(self):
print "My name is", self.get_name()
return SpeakingAnimal
class Dog(object):
def get_name(self):
return "Spot"
class Cat(object):
def get_name(self):
return "Whiskers"
SpeakingDog = make_speaking_animal_class(Dog)
SpeakingCat = make_speaking_animal_class(Cat)
SpeakingCat().speak()
My name is Speaking Whiskers
However as mentioned, the class's __name__ attribute may not be what you expect.
print SpeakingDog
print SpeakingDog()
<class '__main__.SpeakingAnimal'>
<__main__.SpeakingAnimal object at 0x1004a3b50>
You can fix this by assigning them unique __name__ attributes yourself:
SpeakingDog.__name__ = 'SpeakingDog'
print SpeakingDog
<class '__main__.SpeakingDog'>
(Credit to Andrew Jaffe for suggesting this in an answer, but he deleted it.)
There's another way to create a class dynamically, but I discourage you from using it unless you need to; it's even less clear. The type function has a second use, apart from its main one of determining the class of an object: it can be used to dynamically create a new class.
When used this way, the type function takes three parameters:
name, the __name__ the new class will have.
bases, a tuple of of base classes that the new class will inherit from.
dict, a dictionary containing the methods and attributes the new class will have.
You could use it like this:
def make_speaking_animal_class(SpeechlessAnimal, name):
def get_name(self):
return "Speaking " + super(SpeakingAnimal, self).get_name()
def speak(self):
print "My name is", self.get_name()
bases = (SpeechlessAnimal,)
# We need to define SpeakingAnimal in a variable so that get_name can refer
# to it for the super() call, otherwise we could just return it directly.
SpeakingAnimal = type(name, bases, {
'get_name': get_name,
'speak': speak
})
return SpeakingAnimal
class Dog(object):
def get_name(self):
return "Spot"
class Cat(object):
def get_name(self):
return "Whiskers"
SpeakingDog = make_speaking_animal_class(Dog, 'SpeakingDog')
SpeakingCat = make_speaking_animal_class(Cat, 'SpeakingCat')
SpeakingDog().speak()
SpeakingCat().speak()
My name is Speaking Spot
My name is Speaking Whiskers