Rename method in class body - python

Is it possible to rename method in Python?
Something like this
class Validation(unittest.TestCase):
def __init__(self, methodName='runTest'):
super(Validation, self).__init__(methodName)
#Doesn't work
setattr(self,'do_folders_equal','test_do_folders_equal')
Rename method 'do_folders_equal' to 'test_do_folders_equal'..
Usage above , obviously, is incorrect...How to do this hack?

You wouldn't rename it, you'd just assign it to another variable:
class Validation(unittest.TestCase):
def __init__(self, methodName='runTest'):
super(Validation, self).__init__(methodName)
self.test_do_folders_equal = self.do_folders_equal
However there can sometimes be some weird interplay with inherited methods etc, so it sort of depends what you are trying to do. Not sure how this behaves on old-style classes either...
EDIT: Or using a name read at runtime:
class Validation(unittest.TestCase):
def __init__(self, methodName='runTest'):
super(Validation, self).__init__(methodName)
setattr(self, 'test_do_folders_equal', getattr(self, 'do_folders_equal'))

Related

How can I add an additional class inheritance to an existing one, but with different number of arguments to existing inheritance?

I have an existing class TSEparser that inherits from a parent class Subparser, but now I want to add another parent class (SubparserMixin) to class TSEparser. However, the problem is that the arguments passed by the initial inheritance gets in the way of this new inheritance- how can I fix this?
i.e:
class Subparser:
def __init__(self, filename, data_group, **kwargs):
self.data_group = data_group
self.filename = filename
self.__dict__.update(kwargs)
class TSEparser(Subparser):
def __init__(self, filename, data_group, **kwargs):
super().__init__(filename, data_group, **kwargs)
Now I want to add another parent class SubparserMixin so we have class TSEparser(Subparser, SubparserMixin), however Subparsermixin looks like this:
class SubparserMixin:
def __init__(self):
self.subparsers = {}
self.context = PacketContext
Is there some way I can inherit separately from both Parent Classes? Something like this:
class TSEparser(Subparser):
def __init__(self, filename, data_group, **kwargs):
super(Subparser).__init__(filename, data_group, **kwargs)
super(SubparserMixin).__init__()
I know the syntax is not correct but I hope it is clear what I am trying to do!
You can specify which class' constructor gets called by simply using its name. So in your example you can just do
class TSEparser(Subparser, SubparserMixin):
def __init__(self, filename, data_group, **kwargs):
Subparser.__init__(self, filename, data_group, **kwargs)
SubparserMixin.__init__(self)
There is no good and easy way to make this work using super. You can check out this question for more detail. super normally takes care of calling the proper classes for you. If you want to call the __init__ of a specific class manually (e.g. because the required arguments are different), you need to call it directly using its name as shown above.
Edit: My code example mistakenly didn't pass self when calling the base __init__ methods. Note that this is a difference between calling by super and directly by base class. super doesn't require self, while direct calling does.
# without self
super().__init__()
# with self
Base.__init__(self)

Refer to a superclass from the class body

I've got some code where I need to refer to a superclass when defining stuff in a derived class:
class Base:
def foo(self):
print('foo')
def bar(self):
print('bar')
class Derived_A(Base):
meth = Base.foo
class Derived_B(Base):
meth = Base.bar
Derived_A().meth()
Derived_B().meth()
This works, but I don't like verbatim references to Base in derived classes. Is there a way to use super or alike for this?
You can't do that.
class keyword in Python is used to create classes which are instances of type type. In it's simplified version, it does the following:
Python creates a namespace and executes the body of the class in that namespace so that it will be populated with all methods and attributes and so on...
Then calls the three-arguments form of type(). The result of this call is your class which is then assign to a symbol which is the name of your class.
The point is when the body of the class is being executed. It doesn't know about the "bases". Those bases are passed to the type() after that.
I also explained the reasons why you can't use super() here.
Does this work for you?
class Base:
def foo(self):
print('foo')
def bar(self):
print('bar')
class Derived_A(Base):
def __init__(self):
self.meth = super().foo
class Derived_B(Base):
def __init__(self):
self.meth = super().bar
a = Derived_A().meth()
b = Derived_B().meth()
You'll need to lookup the method on the base class after the new type is created. In the body of the class definition, the type and base classes are not accessible.
Something like:
class Derived_A(Base):
def meth(self):
return super().foo()
Now, it is possible to do some magic behind the scenes to expose Base to the scope of the class definition as its being executed, but that's much dirtier, and would mean that you'd need to supply a metaclass in your class definition.
Since you want "magic", there is still one sane option we can take before diving into metaclasses. Requires Python 3.9+
def alias(name):
def inner(cls):
return getattr(cls, name).__get__(cls)
return classmethod(property(inner))
class Base:
def foo(self):
...
class Derived_A(Base):
meth = alias("foo")
Derived_A().meth() # works
Derived_A.meth() # also works
Yes, this does require passing the method name as a string, which destroys your IDE and typechecker's ability to reason about it. But there isn't a good way to get what you are wanting without some compromises like that.
Really, a bit of redundancy for readability is probably worth it here.

Why is Python super used in the child's init method?

According to Python docs super()
is useful for accessing inherited methods that have been overridden in
a class.
I understand that super refers to the parent class and it lets you access parent methods. My question is why do people always use super inside the init method of the child class? I have seen it everywhere. For example:
class Person:
def __init__(self, name):
self.name = name
class Employee(Person):
def __init__(self, **kwargs):
super().__init__(name=kwargs['name']) # Here super is being used
def first_letter(self):
return self.name[0]
e = Employee(name="John")
print(e.first_letter())
I can accomplish the same without super and without even an init method:
class Person:
def __init__(self, name):
self.name = name
class Employee(Person):
def first_letter(self):
return self.name[0]
e = Employee(name="John")
print(e.first_letter())
Are there drawbacks with the latter code? It looks so much cleanr to me. I don't even have to use the boilerplate **kwargs and kwargs['argument'] syntax.
I am using Python 3.8.
Edit: Here's another stackoverflow questions which has code from different people who are using super in the child's init method. I don't understand why. My best guess is there's something new in Python 3.8.
The child might want to do something different or more likely additional to what the super class does - in this case the child must have an __init__.
Calling super’s init means that you don’t have to copy/paste (with all the implications for maintenance) that init in the child’s class, which otherwise would be needed if you wanted some additional code in the child init.
But note there are complications about using super’s init if you use multiple inheritance (e.g. which super gets called) and this needs care. Personally I avoid multiple inheritance and keep inheritance to aminimum anyway - it’s easy to get tempted into creating multiple levels of inheritance/class hierarchy but my experience is that a ‘keep it simple’ approach is usually much better.
The potential drawback to the latter code is that there is no __init__ method within the Employee class. Since there is none, the __init__ method of the parent class is called. However, as soon as an __init__ method is added to the Employee class (maybe there's some Employee-specific attribute that needs to be initialized, like an id_number) then the __init__ method of the parent class is overridden and not called (unless super.__init__() is called) and then an Employee will not have a name attribute.
The correct way to use super here is for both methods to use super. You cannot assume that Person is the last (or at least, next-to-last, before object) class in the MRO.
class Person:
def __init__(self, name, **kwargs):
super().__init__(**kwargs)
self.name = name
class Employee(Person):
# Optional, since Employee.__init__ does nothing
# except pass the exact same arguments "upstream"
def __init__(self, **kwargs):
super().__init__(**kwargs)
def first_letter(self):
return self.name[0]
Consider a class definition like
class Bar:
...
class Foo(Person, Bar):
...
The MRO for Foo looks like [Foo, Person, Bar, object]; the call to super().__init__ inside Person.__init__ would call Bar.__init__, not object.__init__, and Person has no way of knowing if values in **kwargs are meant for Bar, so it must pass them on.

logging module with setters in class

I have a question about logging module, I'm using it in some classes and I'm wondering how I can log setters or getters. For example I have a class :
class Item(object):
def __init__(self, name):
self.name = name
item_ = Item('object')
item_.name = 'New object'
I want here a log to say name of object has been changed. Of course, I would like avoid using #property and setters functions which will override my class.
This is relatively straightforward to implement:
class Item(object):
def __setattr__(self, name, value):
super(Item, self).__setattr__(name, value)
# put logging code here
Do not use vars() for this. It will not handle #property and other data descriptors correctly. It will also return a read-only dictionary-like-thing if you happen to be implementing a metaclass. If you don't know what that means, then you're not implementing a metaclass, so don't worry.
If you have multiple classes like this, you can factor this functionality out into a mixin class:
class LoggingMixin(object):
def __setattr__(self, name, value):
super(LoggingMixin, self).__setattr__(name, value)
# put logging code here
Now, when you want to create a new class that needs attribute logging, simply inherit from LoggingMixin, along with any other base classes if necessary.

Python calling super constructor - am I doing it right?

I have a base class like so:
class Token:
def __init__(self, value):
self.value = value.strip()
self.tokens = None
def get_value(self):
return self.value
def tokenize(self):
pass # abstract stub
def __str__(self):
return type(self).__name__ + ': '+ re.sub(r'\s+', ' ', self.value)
And a ton of it's child classes:
class T_DefineDirective(Token):
def __init__(self, value):
super().__init__(value)
class T_IncludeDirective(Token):
def __init__(self, value):
super().__init__(value)
class T_IfdefDirective(Token):
def __init__(self, value):
super().__init__(value)
class T_Identifier(Token):
def __init__(self, value):
super().__init__(value)
class T_Rvalue(Token):
def __init__(self, value):
super().__init__(value)
def tokenize(self):
pass # stuff here
Now I'm a DRY programmer. I hate repetition. If you look at the code, the __init__ piece is copy-pasted in all the child classes.
My question, is there some way to avoid the repetition, or is this really the right way?
(note that the example is a bit shortened, so it may not make too much sense. But you can see the issue I mean).
If you do not have any additional setup work to do in the Token subclasses, then it is safe not to override __init__.
If you do have to perform some subclass-specific initialisation, then the patten that you're using is fine and 'pythonic'.
To clarify:
if __init__ is not defined on a class, then Python will use the __init__ method defined on (one of) its parent class(es), if possible
this is because there aren't any special rules for overriding 'magic' methods like __init__
even if the initialiser on a parent class is used, an instance of the subclass will be created
this is because the actual creation happens in __new__; the newly created object is then passed to __init__ for initialisation
If you really want to eliminate as much boilerplate as possible:
First, you don't need __init__ if all it does is call super(); special methods are inherited just like any other methods, as sapi's answer explains.
Second, you can dynamically create a bunch of classes:
token_classes = {
'T_{}'.format(name): type('T_{}'.format(name), (Token,), {})
for name in 'DefineDirective IncludeDirective IfdefDirective Identifier'.split()
}
And you can use them straight out of that dict, but if you really want to make them into globals you can:
globals().update(token_classes)
However, the whole goal of avoiding repetition is to make your code more readable and maintainable, and in this case, I think we're achieving the opposite. :)

Categories