Access Class Name From a Class Method - python

assuming the simple case where Foo is
class Foo:
def some_func(self):
print('hellow world')
I only have access to the variable func where func is :
func = Foo.some_func
I am trying to get the Foo class name from the variable func
func
Out[6]: <function __main__.Foo.some_func>
func.__class__.__name__
Out[7]: 'function'
I am expecting to get Foo is there anyway to do that?

Python 3 solution:
def get_class_name(func):
return func.__qualname__.split('.')[0]
__qualname__ method actually prints Foo.some_func for func.
Splitting the string by . and taking the first element, it should do the job.
Python 2 & 3 solution:
def get_class_name(func):
return func.__str__().split('.')[0].split()[-1]
Edit:
In Python 3, func.__str__() prints <function Foo.some_func at 0x10c456b70>.
In Python 2, func.__str__() prints <unbound method Foo.some_func>.

Related

How can I definine a class inside a function, then access it from outside said function in Python?

Let's assume I have the following code:
#!/usr/bin/python3
def genclass():
class stuff:
foo = 'Foo'
bar = 'Bar'
genclass()
How would I access that class outside of the function? I tried the following:
Just print the function:
print(stuff.foo)
Print it with the function name:
print(genclass.stuff.foo)
Same, but with () to indicate that genclass is a function:
print(genclass().stuff.foo)
Unfortunately, none of the above worked.
One way is you can store the class objects in a list and return the list, but you can only access the classes by index.
def genclass():
class stuff:
foo = 'foo'
bar = 'bar'
class other:
one = 'one'
two = 'two'
return [stuff, other]
>>> genclass()[0].foo
'foo'
>>> genclass()[1].one
'one'
But this is basically treating the function as a list.
edit::
You can also use it as both a function that executes another command if you include an if-statement to check if a parameter is passed.
def genclass(arg = None):
class stuff:
foo = 'foo'
bar = 'bar'
if arg:
print('parameter passed')
else:
return [stuff]
>>> genclass()[0].bar
'bar'
>>> genclass(1)
parameter passed
What you want to do is setting an attribute in a function. You can't do it directly, but you need to call the setattr function.
You can do something like:
def genclass():
class stuff:
foo = 'Foo'
bar = 'Bar'
setattr(genclass, 'stuff', stuff)
And then, after executing the function, because everything inside a function is ran just after its execution, you can access the class by typing
genclass.stuff
Anyway, this is a workaround and I don't think it is a best practice.
For better understand what a function is in Python and why you can set them an attribute please have a look on https://www.tutorialspoint.com/What-are-Python-function-attributes

Python override __str__ outside of class definition

Say I have a module that I've imported that works fine but doesn't provide a good __str__ method and I want to add one.
In some hybrid language:
from foo import Foo
Foo.prototype._str__ = function myNIcePrintFunction(){
}
How do I do this please in Python please?
You can first define a function, and then monkey-patch it.
Like:
def foostr(self):
return 'A foo str'
Foo.__str__ = foostr
Or with a lambda expression:
Foo.__str__ = lambda self: 'Another foo str'
There is however a small difference in the fact that this method will have a different Foo.__str__.__name__, etc. But the str(..) builtin function will work as expected. As #chepner says, we can use the wrap function from functools to preserve most meta-data:
from functools import wraps
def foostr(self):
return 'A foo str'
Foo.__str__ = wraps(Foo.__str__)(foostr)
But then Foo.__str__.__qualname__ will for example result in 'superfoo.__str__' (with superfoo the first class in the method resolution order (MRO) of Foo that implemented __str__).
For example:
>>> class Foo:
... pass
...
>>> str(Foo())
'<__main__.Foo object at 0x7f807046e668>'
>>> Foo.__str__ = lambda self: 'another Foo str'
>>> str(Foo())
'another Foo str'
If you have access to the implementation of the Foo class, it is however better to just define a __str__ function in the class, and not monkey patch it later in the process.
You can override any class function like this, while maintaining the original functionalities:
class a():
pass
def monkey(fn):
def newfn(self,*args, **kw):
print('hijacked!')
return fn(self,*args, **kw)
return newfn
a.__str__ = monkey(a.__str__)
b = a()
print(str(b))
>>> hijacked!
>>> <__main__.a object at 0x7f61f19faa58>

Unbound method in Python [duplicate]

This question already has answers here:
Python newbie having a problem using classes
(2 answers)
Closed 7 years ago.
In this link while explaining classes, for the following sample class
class MyClass:
"""A simple example class"""
i = 12345
def f(self):
return 'hello world'
it is mentioned that
then MyClass.i and MyClass.f are valid attribute references, returning an integer and a function object, respectively
but when I try to print MyClass.f it gives me a error <unbound method MyClass.f>.
So what does MyClass.f returning function object really mean in this context.
It's telling you that the method isn't bound to any instance.
If you did this:
myinstance = MyClass()
myinstance.f() # "hello world"
It would work, or, you can make it static:
class MyClass
#staticmethod
def f()
print "hello world"
MyClass.f()
Would also work
Instance it:
>>> your_class = MyClass()
>>> your_class.f()
'hello world'
It's bound when you instance it:
>>> your_class.f
<bound method MyClass.f of <__main__.MyClass instance at 0x1244ef0>>
It is an instance method. You should use classmethod or staticmethod, like this:
>>> class A():
... #staticmethod
... def f(*args):
... return "hello"
...
>>> A.f
<function f at 0xb744541c>
>>> print A.f
<function f at 0xb744541c>
>>> print A.f()
hello

Can a function in python behave like a class?

In python a function is a first class object. A class can be called. So you can replace a function with a class. But can you make a function behave like a class? Can you add and remove attributes or call inner functions( then called methods) in a function?
I found a way to do this via code inspection.
import inspect
class AddOne(object):
"""class definition"""
def __init__(self, num):
self.num = num
def getResult(self):
"""
class method
"""
def addOneFunc(num):
"inner function"
return num + 1
return addOneFunc(self.num);
if __name__ == '__main__':
two = AddOne(1);
two_src = '\n'.join([line[4:] for line in inspect.getsource(AddOne.getResult).split('\n')])
one_src = '\n'.join([line[4:] for line in two_src.split('\n')
if line[:4] == ' ' and line[4:8] == ' ' or line[4:8] == 'def '])
one_co = compile(one_src, '<string>', 'exec')
exec one_co
print addOneFunc(5)
print addOneFunc.__doc__
But is there a way to access the local variables and functions defined in a class in a more direct way?
EDIT
The question is about how to access the inner structure of python to get a better understanding. Of course I wouldn't do this in normal programming. The question arose when we had a discussion about private variables in python. My opinion was this to be against the philosophy of the language. So someone came up with the example above. At the moment it seems he is right. You cannot access the function inside a function without the inspect module, rendering this function private. With co_varnames we are awfully close because we already have the name of the function. But where is the namespace dictionary to hold the name. If you try to use
getResult.__dict__
it is empty. What I like to have is an answer from python like
function addOneFunc at <0xXXXXXXXXX>
You can consider a function to be an instance of a class that only implements __call__, i.e.
def foo(bar):
return bar
is roughly equivalent to
class Foo(object):
def __call__(self, bar):
return bar
foo = Foo()
Function instances have a __dict__ attribute, so you can freely add new attributes to them.
Adding an attribute to a function can be used, for example, to implement a memoization decorator, which caches previous calls to a function:
def memo(f):
#functools.wraps(f)
def func(*args):
if args not in func.cache: # access attribute
func.cache[args] = f(*args)
return func.cache[args]
func.cache = {} # add attribute
return func
Note that this attribute can also be accessed inside the function, although it can't be defined until after the function.
You could therefore do something like:
>>> def foo(baz):
def multiply(x, n):
return x * n
return multiply(foo.bar(baz), foo.n)
>>> def bar(baz):
return baz
>>> foo.bar = bar
>>> foo.n = 2
>>> foo('baz')
'bazbaz'
>>> foo.bar = len
>>> foo('baz')
6
(although it's possible that nobody would thank you for it!)
Note, however, that multiply, which was not made an attribute of foo, is not accessible from outside the function:
>>> foo.multiply(1, 2)
Traceback (most recent call last):
File "<pyshell#20>", line 1, in <module>
foo.multiply(1, 2)
AttributeError: 'function' object has no attribute 'multiply'
The other question addresses exactly what you're trying to do:
>>> import inspect
>>> import new
>>> class AddOne(object):
"""Class definition."""
def __init__(self, num):
self.num = num
def getResult(self):
"""Class method."""
def addOneFunc(num):
"inner function"
return num + 1
return addOneFunc(self.num)
>>> two = AddOne(1)
>>> for c in two.getResult.func_code.co_consts:
if inspect.iscode(c):
print new.function(c, globals())
<function addOneFunc at 0x0321E930>
Not sure if the following is what you're thinking about, but you can do this:
>>> def f(x):
... print(x)
...
>>> f.a = 1
>>> f.a
1
>>> f(54)
54
>>> f.a = f
>>> f
<function f at 0x7fb03579b320>
>>> f.a
<function f at 0x7fb03579b320>
>>> f.a(2)
2
So you can assign attributes to a function, and those attributes can be variables or functions (note that f.a = f was chosen for simplicity; you can assign f.a to any function of course).
If you want to access the local variables inside the function, I think then it's more difficult, and you may indeed need to revert to introspection. The example below uses the func_code attribute:
>>> def f(x):
... a = 1
... return x * a
...
>>> f.func_code.co_nlocals
2
>>> f.func_code.co_varnames
('x', 'a')
>>> f.func_code.co_consts
(None, 1)

Python: calling a function as a method of a class

Let's start with some code:
def func(*x):
print('func:', x)
class ABC:
def __init__(self, f):
self.f1 = f
def f2(*x):
print('f2:', x)
Now we do some tests:
>>> a = ABC(func)
>>> a.f1(10)
func: (10,)
>>> a.f2(10)
f2: (<__main__.ABC object at 0xb75381cc>, 10)
>>> a.f3 = func
>>> a.f3(10)
func: (10,)
>>> a.f1
<function func at 0xb74911ec>
>>> a.f2
<bound method ABC.f2 of <__main__.ABC object at 0xb75381cc>>
>>> a.f3
<function func at 0xb74911ec>
Note that func is a normal function and we are making it a method f1 of the class.
We can see that f2 is getting the class instance as the first argument, but f1 and f3 are not, even though all functions are called as class methods. We can also see that if we call a normal function as a method of a class, Python does not make a bound method from it.
So why is f1 or f3 NOT getting a class instance passed to it even when we are calling it as a method of a class? And also, how does Python know that we are calling an outer function as a method so that it should not pass an instance to it.
-- EDIT --
OK, so basically what I am doing wrong is that I am attaching the functions on the instance and NOT on the class object itself. These functions therefore simply become instance attributes. We can check this with:
>>> ABC.__dict__
... contents...
>>> a.__dict__
{'f1': <function func at 0xb74911ec>, 'f3': <function func at 0xb74911ec>}
Also note that this dict can not be assigned to:
>>> ABC.__dict__['f4'] = func
TypeError: 'dict_proxy' object does not support item assignment
You kind of partially answered your own question inspecting the object. In Python, objects behave like namespaces, so the first attribute points to a function and the second points to a method.
This is how you can add a method dynamically:
from types import MethodType
def func(*x):
print('func:', x)
class ABC:
def __init__(self, f):
self.f1 = MethodType(f, self, self.__class__)
def f2(*x):
print('f2:', x)
if __name__ == '__main__':
a = ABC(func)
print a.f1(10)
print a.f2(10)
a.f3 = MethodType(func, a, ABC)
print a.f3(10)
Note that it will bind the method to your instance, not to the base class. In order to monkeypatch the ABC class:
>>> ABC.f4 = MethodType(func, None, ABC)
>>> a.f4(1)
('func:', (<__main__.ABC instance at 0x02AA8AD0>, 1))
Monkeypatching is usually frowned upon in the Python circles, despite being popular in other dynamic languages (notably in Ruby when the language was younger).
If you ever resort to this powerful yet dangerous technique, my advice is:
never, ever override an existing class method. just don't.
That's because f1 and f3 are not class method they are just references to a global function defined in __main__:
In [5]: a.f1
Out[5]: <function __main__.func>
In [8]: a.f3
Out[8]: <function __main__.func>
In [9]: a.f2
Out[9]: <bound method ABC.f2 of <__main__.ABC instance at 0x8ac04ac>>
you can do something like this to make a global function a class method:
In [16]: class ABC:
def __init__(self,f):
ABC.f1=f
def f2(*x):
print('f2',x)
....:
In [17]: a=ABC(func)
In [18]: a.f1(10)
('func:', (<__main__.ABC instance at 0x8abb7ec>, 10))

Categories