Getting class containing method - python

I'm writing a method decorator and require access to the class defining the method that is currently decorated.
The issue seems with this is, that with Python 3 methods in a class are just functions unless the class is instantiated.
Is there any way around this? I don't really want to fiddle around with __qualname__...
In [29]: class A:
....: def B(self):
....: pass
....:
In [30]: A.B.__qualname__
Out[30]: 'A.B'
# This is what I want:
>>> get_class(A.B)
A

You can't, because at the time your decorator on a method is run, the class is yet to be created.
An example illustrates this a little better:
class Foo:
#spam
def bar(self): pass
When spam(bar) is invoked to produce a decorated function, we are inside the pseudo-function that Python runs to define the class body. Only when that pseudo-function is done executing, is the local namespace of that function turned into the class body and the actual class object itself is created.
That means that there is no Foo class object yet at the time spam() is run.
Instead, create a class decorator:
#spam
class Foo:
def bar(self): pass
Now spam() is passed the whole, complete Foo class giving you access to both the class and the methods.
If you need to mark specific methods on the class for decoration, you could use a marker decorator that sets attributes on the function:
def marker(func):
func._marked = True
return func
Use this decorator in the class body on methods that you want decorated, then use the class decorator to pick out those methods:
#spam
class Foo:
#marker
def bar(self): pass
def baz(self): pass

Related

Assign Python class variable with a method of that class

I have a code like this:
def my_func():
pass
class MyClass():
class_variable = my_func()
pass
i = MyClass()
Since my_func is logically related to MyClass, and doesn't serve any purpose outside of it, I'd prefer to have something like that instead:
class MyClass():
class_variable = my_func()
def my_func():
pass
i = MyClass()
The above doesn't work, of course, because my_func isn't defined yet at the time it's called. Is there some other way to assign a class variable from inside the class?
I'd personally keep my_func() outside the class. Sure, it may only be used for the class definition, but if it is not useful once the class is defined, it should not be part of the class API.
If the my_func() function should be used together with the class even after the class has been created, then you can still make this work with MyClass.my_func() being a static method. In that case define the function first before setting the class variable:
class MyClass():
#staticmethod
def my_func():
pass
class_variable = my_func.__func__()
The #staticmethod is not strictly necessary as at the time class_variable is set, my_func is still a local name in the class definition body.
However, since you are using it as as static function anyway, you may as well mark it as such. The advantage however is that MyClass.my_func() now also works.
Because a #staticmethod isn't going to bind outside of a class or instance attribute context, you do need to unwrap it first by accessing the wrapped function with the __func__ attribute.
Demo:
>>> class MyClass():
... #staticmethod
... def my_func():
... return 'foobar'
... class_variable = my_func.__func__()
...
>>> MyClass.class_variable
'foobar'
>>> MyClass.my_func()
'foobar'

How to add a classmethod in Python dynamically

I'm using Python 3.
I know about the #classmethod decorator. Also, I know that classmethods can be called from instances.
class HappyClass(object):
#classmethod
def say_hello():
print('hello')
HappyClass.say_hello() # hello
HappyClass().say_hello() # hello
However, I don't seem to be able to create class methods dynamically AND let them be called from instances. Let's say I want something like
class SadClass(object):
def __init__(self, *args, **kwargs):
# create a class method say_dynamic
SadClass.say_dynamic() # prints "dynamic!"
SadClass().say_dynamic() # prints "dynamic!"
I've played with cls.__dict__ (which produces exceptions), and with setattr(cls, 'say_dynamic', blahblah) (which only makes the thingie callable from the class and not the instance).
If you ask me why, I wanted to make a lazy class property. But it cannot be called from instances.
#classmethod
def search_url(cls):
if hasattr(cls, '_search_url'):
setattr(cls, '_search_url', reverse('%s-search' % cls._meta.model_name))
return cls._search_url
Maybe because the property hasn't been called from the class yet...
In summary, I want to add a lazy, class method that can be called from the instance... Can this be achieved in an elegant (nottoomanylines) way?
Any thoughts?
How I achieved it
Sorry, my examples were very bad ones :\
Anyway, in the end I did it like this...
#classmethod
def search_url(cls):
if not hasattr(cls, '_search_url'):
setattr(cls, '_search_url', reverse('%s-search' % cls._meta.model_name))
return cls._search_url
And the setattr does work, but I had made a mistake when testing it...
You can add a function to a class at any point, a practice known as monkey-patching:
class SadClass:
pass
#classmethod
def say_dynamic(cls):
print('hello')
SadClass.say_dynamic = say_dynamic
>>> SadClass.say_dynamic()
hello
>>> SadClass().say_dynamic()
hello
Note that you are using the classmethod decorator, but your function accepts no arguments, which indicates that it's designed to be a static method. Did you mean to use staticmethod instead?
If you want to create class methods, do not create them in the __init__ function as it is then recreated for each instance creation. However, following works:
class SadClass(object):
pass
def say_dynamic(cls):
print("dynamic")
SadClass.say_dynamic = classmethod(say_dynamic)
# or
setattr(SadClass, 'say_dynamic', classmethod(say_dynamic))
SadClass.say_dynamic() # prints "dynamic!"
SadClass().say_dynamic() # prints "dynamic!"
Of course, in the __init__ method the self argument is an instance, and not the class: to put the method in the class there, you can hack something like
class SadClass(object):
def __init__(self, *args, **kwargs):
#classmethod
def say_dynamic(cls):
print("dynamic!")
setattr(self.__class__, 'say_dynamic', say_dynamic)
But it will again reset the method for each instance creation, possibly needlessly. And notice that your code most probably fails because you are calling the SadClass.say_dynamic() before any instances are created, and thus before the class method is injected.
Also, notice that a classmethod gets the implicit class argument cls; if you do want your function to be called without any arguments, use the staticmethod decorator.
As a side note, you can just use an instance attribute to hold a function:
>>> class Test:
... pass
...
>>> t=Test()
>>> t.monkey_patch=lambda s: print(s)
>>> t.monkey_patch('Hello from the monkey patch')
Hello from the monkey patch
How I achieved it:
#classmethod
def search_url(cls):
if not hasattr(cls, '_search_url'):
setattr(cls, '_search_url', reverse('%s-search' % cls._meta.model_name))
return cls._search_url

How to refer overriden class functions in python

I know C++ and Java and I am unfamiliar with Pythonic programming. So maybe it is bad style what I am trying to do.
Consider fallowing example:
class foo:
def a():
__class__.b() # gives: this is foo
bar.b() # gives: this is bar
foo.b() # gives: this is foo
# b() I'd like to get "this is bar" automatically
def b():
print("this is foo")
class bar( foo ):
def b( ):
print("this is bar")
bar.a()
Notice, that I am not using self parameters as I am not trying to make instances of classes, as there is no need for my task. I am just trying to refer to a function in a way that the function could be overridden.
What you want is for a to be a classmethod.
class Foo(object):
#classmethod
def a(cls):
Foo.b() # gives: this is foo
Bar.b() # gives: this is bar
cls.b() # gives: this is bar
#staticmethod
def b():
print("this is foo")
class Bar(Foo):
#staticmethod
def b():
print("this is bar")
Bar.a()
I've edited your style to match the Python coding style. Use 4 spaces as your indent. Don't put extra spaces in between parenthesis. Capitalize & CamelCase class names.
A staticmethod is a method on a class that doesn't take any arguments and doesn't act on attributes of the class. A classmethod is a method on a class that gets the class automatically as an attribute.
Your use of inheritance was fine.
Quote from the Execution Model:
The scope of names defined in a class block is limited to the class
block; it does not extend to the code blocks of methods – this
includes generator expressions since they are implemented using a
function scope.
This mean that there is no name b in the scope of function a. You should refer to it via class or instance object.

Python: How to distinguish between inherited methods

Newbie Python question. I have a class that inherits from several classes, and some of the specialization classes override some methods from the base class. In certain cases, I want to call the unspecialized method. Is this possible? If so, what's the syntax?
class Base(object):
def Foo(self):
print "Base.Foo"
def Bar(self):
self.Foo() # Can I force this to call Base.Foo even if Foo has an override?
class Mixin(object):
def Foo(self):
print "Mixin.Foo"
class Composite(Mixin, Base):
pass
x = Composite()
x.Foo() # executes Mixin.Foo, perfect
x.Bar() # indirectly executes Mixin.Foo, but I want Base.Foo
you can specifically make the call you want using the syntax
Base.Foo(self)
in your case:
class Base(object):
# snipped
def Bar(self):
Base.Foo(self) # this will now call Base.Foo regardless of if a subclass
# overrides it
# snipped
x = Composite()
x.Foo() # executes Mixin.Foo, perfect
x.Bar() # prints "Base.Foo"
This works because Python executes calls to bound methods of the form
instance.method(argument)
as if they were a call to an unbound method
Class.method(instance, argument)
so making the call in that form gives you the desired result. Inside the methods, self is just the instance that the method was called on, i.e, the implicit first argument (that's explicit as a parameter)
Note however that if a subclass overrides Bar, then there's nothing (good) that you can effectively do about it AFAIK. But that's just the way things work in python.

What is the Python equivalent of a Ruby class method?

In ruby you can do this:
class A
def self.a
'A.a'
end
end
puts A.a #-> A.a
How can this be done in python. I need a method of a class to be called without it being called on an instance of the class. When I try to do this I get this error:
unbound method METHOD must be called with CLASS instance as first argument (got nothing instead)
This is what I tried:
class A
def a():
return 'A.a'
print A.a()
What you're looking for is the staticmethod decorator, which can be used to make methods that don't require a first implicit argument. It can be used like this:
class A(object):
#staticmethod
def a():
return 'A.a'
On the other hand, if you wish to access the class (not the instance) from the method, you can use the classmethod decorator, which is used mostly the same way:
class A(object):
#classmethod
def a(cls):
return '%s.a' % cls.__name__
Which can still be called without instanciating the object (A.a()).
There are two ways to do this:
#staticmethod
def foo(): # No implicit parameter
print 'foo'
#classmethod
def foo(cls): # Class as implicit paramter
print cls
The difference is that a static method has no implicit parameters at all. A class method receives the class that it is called on in exactly the same way that a normal method receives the instance.
Which one you use depends on if you want the method to have access to the class or not.
Either one can be called without an instance.
You can also access the class object in a static method using __class__:
class A() :
#staticmethod
def a() :
return '{}.a'.format( __class__.__name__ )
At least this works in Python 3.1

Categories

Resources