Python - create additional statements when calling inherited method - python

Let's say I have this:
class Foo:
...
def func():
return 1+2
class Bar(Foo):
...
def another_func():
# additional stuff I want to do when my parent's func() is called
I don't want to override func , but I do want to add some additional statements when it's called. Also, I don't want to change the original Foo.func.
Is it even possible? If not, any idea for a workaround?

There is no way of doing that, the canonical solution would be to overide func und wrap the original function like so:
class Bar(Foo):
...
def func():
# additional stuff I want to do when my parent's func() is called
res = super(Bar, self).func() # super().func() on Py3
# additional stuff I want to do after my parent's func() is called
return res

You need to override the func function and make the call to parent's func from within it. Python has super() for that purpose:
super(type[, object-or-type])
Return a proxy object that delegates method calls to a parent or sibling class of type.
This is useful for accessing inherited methods that have been overridden in a class.
The search order is same as that used by getattr() except that the type itself is skipped.
Example:
class Foo(object):
def func(self):
print "In parent"
class Bar(Foo):
def func(self):
super(Bar, self).func()
print 'In child' # Your additonal stuff
When you will run the func of Bar as:
b = Bar()
b.func()
will print:
In parent # <-- from Foo.func()
In child # <-- from Bar.func()

Related

How to decorate inherited method - Python

I have to decorate a inherited method, but it decorates all inherited methods. Basically I have to create a decorator that will decorate just one method from the class.
The test looks like this
#my_decorator
class TestClass(Subclass):
pass
t = TestClass()
t.say_hi
Let's say my SubClass looks like this
class SubClass():
def __init__(self):
pass
def say_hi():
print("Hi")
def say_wow():
print("wow")
Now I have to make my_decorator, that has to decorate inherited function say_hi() to print("*****") before it prints "Hi"
I tried doing it like this, but than the decorator applies to all methods from SubClass
def my_decorator(cls)
def say_hi():
print("*********")
cls.say_hi()
return say_hi()
Naturally It applies to every function of the subclass, but how do I make it to apply to only a say_hi() function? -It also returns an TypeError "NoneType" object is not callable
First let us fix SubClass, because instance methods require an explicit instance parameter at definition time:
class SubClass():
def __init__(self):
pass
def say_hi(self):
print("Hi")
def say_wow(self):
print("wow")
Now you want the decorator to replace the say_hi method with a method that prints '****' before calling the original method. Le us write a decorator that just does that(*):
def my_decorator(cls):
orig = cls.say_hi # save the original method
def say_hi(self): # define a new one
print('****')
return orig(self) # ... calling the original method
cls.say_hi = say_hi # replace the method in the class
return cls
You can then use:
#my_decorator
class TestClass(SubClass):
pass
t = TestClass()
t.say_hi()
and get as expected:
****
Hi
(*) this is a very simple decorator that can only replace a say_hi(self) method: neither a different name, nor additional parameters, but decorators can be much smarter...
If you want to decorate a method, then decorate the method, not the class that contains it. If you want a new class, then the decorator applied to the class needs to return a class.
def print_banner(f):
def _(*args, **kwargs):
print("****")
f(*args, **kwargs)
return _
class SubClass():
def __init__(self):
pass
#print_banner
def say_hi(self, ):
print("Hi")
def say_wow(self):
print("wow")

In python, can I access a method in class scope (but not in function scope)

For better encapsulation, I want to decorate instance methods with methods inside the same class.
class SomeClass(object):
#staticmethod
def some_decorator(func):
def wrapped(self):
print 'hello'
return func(self)
return wrapped
#some_decorator
def do(self):
print 'world'
x = SomeClass()
x.do()
However, this piece of code raises TypeError: 'staticmethod' object is not callable
Now I make a workaround by defining a class and overload its new method to simulate a function, but it's eventually a class, not a function.
So can I access my functions inside the class scope?
Just get rid of that #staticmethod line. You want some_decorator to behave like a plain function, not like some kind of method.
The decorator is called when the class definition is being executed, before the class object itself exists. The normal method definitions inside a class are actually just plain old functions, they become methods dynamically each time they are called as attributes of the class instance (which turns them into bound methods). But while the class object itself is being built you can treat them as plain functions.
class SomeClass(object):
def some_decorator(func):
def wrapped(self):
print 'hello'
return func(self)
return wrapped
#some_decorator
def do(self):
print 'world'
x = SomeClass()
x.do()
output
hello
world
BTW, you have an error in your decorator: it's returning wrapped() instead of wrapped.
As chepner mentions in the comments we can delete some_decorator so that it doesn't take up space in the class object after we've finished using it in the class definition. (If we accidentally try to call it we'll get an error). We could do del SomeClass.some_decorator after the class definition, but it's also perfectly valid to put a del statement inside the class definition:
class SomeClass(object):
def some_decorator(func):
def wrapped(self):
print 'hello'
return func(self)
return wrapped
#some_decorator
def do(self):
print 'world'
del some_decorator

Class instance as static attribute

Python 3 doesn't allow you to reference a class inside its body (except in methods):
class A:
static_attribute = A()
def __init__(self):
...
This raises a NameError in the second line because 'A' is not defined.
Alternatives
I have quickly found one workaround:
class A:
#property
#classmethod
def static_property(cls):
return A()
def __init__(self):
...
Although this isn't exactly the same since it returns a different instance every time (you could prevent this by saving the instance to a static variable the first time).
Are there simpler and/or more elegant alternatives?
EDIT:
I have moved the question about the reasons for this restriction to a separate question
The expression A() can't be run until the class A has been defined. In your first block of code, the definition of A is not complete at the point you are trying to execute A().
Here is a simpler alternative:
class A:
def __init__(self):
...
A.static_attribute = A()
When you define a class, Python immediately executes the code within the definition. Note that's different than defining a function where Python compiles the code, but doesn't execute it.
That's why this will create an error:
class MyClass(object):
a = 1 / 0
But this won't:
def my_func():
a = 1 / 0
In the body of A's class definition, A is not yet defined, so you can't reference it until after it's been defined.
There are several ways you can accomplish what you're asking, but it's not clear to me why this would be useful in the first place, so if you can provide more details about your use case, it'll be easier to recommend which path to go down.
The simplest would be what khelwood posted:
class A(object):
pass
A.static_attribute = A()
Because this is modifying class creation, using a metaclass could be appropriate:
class MetaA(type):
def __new__(mcs, name, bases, attrs):
cls = super(MetaA, mcs).__new__(mcs, name, bases, attrs)
cls.static_attribute = cls()
return cls
class A(object):
__metaclass__ = MetaA
Or you could use descriptors to have the instance lazily created or if you wanted to customize access to it further:
class MyDescriptor(object):
def __get__(self, instance, owner):
owner.static_attribute = owner()
return owner.static_attribute
class A(object):
static_attribute = MyDescriptor()
Using the property decorator is a viable approach, but it would need to be done something like this:
class A:
_static_attribute = None
#property
def static_attribute(self):
if A._static_attribute is None:
A._static_attribute = A()
return A._static_attribute
def __init__(self):
pass
a = A()
print(a.static_attribute) # -> <__main__.A object at 0x004859D0>
b = A()
print(b.static_attribute) # -> <__main__.A object at 0x004859D0>
You can use a class decorator:
def set_static_attribute(cls):
cls.static_attribute = cls()
return cls
#set_static_attribute
class A:
pass
Now:
>>>> A.static_attribute
<__main__.A at 0x10713a0f0>
Applying the decorator on top of the class makes it more explicit than setting static_attribute after a potentially long class definition. The applied decorator "belongs" to the class definition. So if you move the class around in your source code you will more likely move it along than an extra setting of the attribute outside the class.

do I need to initialize the parent class when inheriting?

If I am inheriting from a class and not changing anything in a method, is it required to use super to initialize the method from the parent class?
class A:
def __init__(self):
self.html = requests.get("example.com").text
class B(A):
def __init__(self):
# is this needed?
super(B, self).__init__()
def new_method(self):
print self.html
Because you created a __init__ method in your class B, it overrides the method in class A. If you want it executed, you'll have to use super(), yes.
However, if you are not doing anything else in B.__init__, you may as well just omit it:
class A:
def __init__(self):
self.html = requests.get("example.com").text
class B(A):
def new_method(self):
print self.html
If you want to do anything in addition to what A.__init__() does, then it makes sense to create a B.__init__() method, and from that method, invoke the parent __init__.
It's not needed to define the overriding method at all. Python's default behavior is to call the method on the parent class (the next class in the method resolution order) if the current class doesn't override it.
>>> class Foo(object):
... def __init__(self):
... print("Foo")
...
>>> class Bar(Foo): pass
...
>>> Bar()
Foo
<__main__.Bar object at 0x7f5ac7d1b990>
Notice "Foo" got printed when I initialized a Bar instance.
If you do define the method, you need to call the super class's method (either explicitly or via super) if you want to make sure that it gets called.

How to access the parent class during initialisation in python?

How do I find out which class I am initialising a decorator in? It makes sense that I wouldn't be able to find this out as the decorator is not yet bound to the class, but is there a way of getting round this?
class A(object):
def dec(f):
# I am in class 'A'
def func(cls):
f(cls)
return func
#dec
def test(self):
pass
I need to know which class I am (indicated by the commented line).
I don't think this is possible. At the very moment when you define test, the class doesn't exist yet.
When Python encounters
class A(object):
it creates a new namespace in which it runs all code that it finds in the class definition (including the definition of test() and the call to the decorator), and when it's done, it creates a new class object and puts everything into this class that was left in the namespace after the code was executed.
So when the decorator is called, it doesn't know anything yet. At this moment, test is just a function.
I don't get the question.
>>> class A(object):
def dec(f):
def func(cls):
print cls
return func
#dec
def test(self):
pass
>>> a=A()
>>> a.test()
<__main__.A object at 0x00C56330>
>>>
The argument (cls) is the class, A.
As Nadia pointed out you will need to be more specific. Python does not allow this kind of things, which means that what you are trying to do is probably something wrong.
In the meantime, here is my contribution: a little story about a sailor and a frog. (use a constructor after the class initialization)
class Cruise(object):
def arewelostyet(self):
print 'Young sailor: I think I am lost, help me :s'
instance = Cruise()
instance.arewelostyet()
def whereami(lostfunc):
"""
decorator
"""
def decorated(*args, **kwargs):
lostfunc(*args, **kwargs)
print 'Frog: Crôak! thou art sailing in class', lostfunc.im_class.__name__
# don't forget to write name and doc
decorated.func_name = lostfunc.func_name
decorated.func_doc = lostfunc.func_name
return decorated
print '[i]A frog pops out of nowhere[/i]'
# decorate the method:
Cruise.arewelostyet = whereami(Cruise.arewelostyet)
instance.arewelostyet()

Categories

Resources