Is it possible to make a module callable with parameters?
I am trying to make a callable module on Python, following the question and its answers Callable modules, like so:
foo.py
import sys
class foo(object):
def __call__(self):
return 'callable'
sys.modules[__name__] = foo()
then I call it:
import foo
print(foo()) # 'callable'
but my objective is to pass a parameter in the callable module:
print(foo('parameter'))
Any ideas how can I accomplish that?
There's nothing special about the class you created (it's not even a ModuleType subclass), so there's nothing special about its __call__ method. If you want to call it with arguments, just add parameters to the __call__ definition:
import sys
class foo(object):
def __call__(self, x):
return f'callable, and called with {x}'
sys.modules[__name__] = foo()
And now, you can pass it an argument, exactly like any other callable object:
import foo
print(foo('hello'))
And the output is:
callable, and called with hello
From the comments, you tried to do this:
def __call__(a, self):
return a
But, like all methods in Python, __call__ wants self to come first. It doesn't care about the names (unless you call it with keyword arguments), just the order: the first parameter gets the receiver (the foo in foo('hello')), even if it you called that parameter a, and the second parameter gets the first normal argument (the 'hello'), even if you called that parameter self.
So, you're passing the module foo as the first parameter, a, and you return a, so it returns foo.
Which is why you got this:
<sta.foo object at 0x10faee6a0>
That isn't an error, that's the perfectly valid output that you get when you print out an instance of a class that doesn't define __repr__ or __str__.
Related
I'm trying to call an external function via a class variable. The following is a simplification of my real code:
def func(arg):
print(arg)
class MyClass(object):
func_ref = None
#classmethod
def setUpClass(cls):
#MyClass.func_ref = func
cls.func_ref = func
#staticmethod
def func_override(arg):
print("override printing arg...")
MyClass.func_ref(arg)
if __name__ == "__main__":
print(type(func))
print(type(MyClass.func_ref))
MyClass.setUpClass()
print(type(MyClass.func_ref))
MyClass.func_override("hello!")
The above code produces the following output:
[~]$ python tmp.py
<type 'function'>
<type 'NoneType'>
<type 'instancemethod'>
override printing arg...
Traceback (most recent call last):
File "tmp.py", line 20, in <module>
MyClass.func_override("hello!")
TypeError: func_override() takes exactly 2 arguments (1 given)
The situation seems to be unchanged if I use MyClass in place of cls within the classmethod setUpClass().
I would expect the type of MyClass.func_ref to be function after the assignment in setUpClass() which explains the TypeError I get when I try to call it. Why is the type of func_ref being changed to instancemethod when the value I assigned to it is of type function?
This only seems to be an issue in Python 2. Python 3 behaves as I would expect.
How do I get calls to the static method MyClass.func_override() to call func()?
UPDATE
I was able to get the above to work by applying the following patch:
## -14,7 +14,7 ## class MyClass(object):
def func_override(arg):
print("override printing arg...")
func(arg)
- MyClass.func_ref.__func__(arg)
+ MyClass.func_ref(arg)
if __name__ == "__main__":
print(type(func))
While the above works, its not at all clear to me why I needed to do this. I still don't understand why the type of func_ref ends up an instancemethod when I assigned to it a value of type function.
Just put the function through a staticmethod as follows:
#classmethod
def setUpClass(cls):
#MyClass.func_ref = func
cls.func_ref = staticmethod(func)
There's no need to play with #-based decorators in this case as you want to modify how the method is bound to MyClass, not the general definition of func.
Why is this necessary? Because, when you assign a method to class, Python assumes you'll want to refer to an instance (via self) or the class (via cls). self, unlike this in JS, is only a naming convention, so when it sees arg it assumes it got an instance, but you passed a string in your call.
So, as as Python cares, you might have as well have written def func(self):. Which is why the message says unbound method func() must be called with MyClass 👉instance👈 as first argument.
staticmethod means, "please leave this alone and don't assume an instance or a class in the first variable".
You can even dispense with the setUpClass entirely:
class MyClass(object):
func_ref = staticmethod(func)
BTW: In 2021, 16 months past EOL, Python 2.7 has all the subtle fagrance of moldy gym socks. Except less safe, virologically-speaking.
When func_ref is called, it's expecting a self argument, just like any other normal (instance) class method (see this question and answers for discussions why). You can either add a self argument to func or make func a static method:
#staticmethod
def func(arg):
print(arg)
>>> MyClass.setUpClass()
>>> MyClass.func_override("hello!")
override printing arg...
hello!
Note that in either case func is now not normally callable as a regular function:
>>> func('what does this do?')
TypeError: 'staticmethod' object is not callable
If you need func to be usable as a regular function, you can wrap it with another, qualifying function and use the wrapper in MyClass:
def func(arg):
print(arg)
#staticmethod
def func_wrapper(arg):
func(arg)
class MyClass(object):
#classmethod
def setUpClass(cls):
cls.func_ref = func_wrapper # use wrapper function
>>> MyClass.setUpClass()
>>> MyClass.func_override("success!")
override printing arg...
success!
I have following class with a function:
class A:
def myfn():
print("In myfn method.")
Here, the function does not have self as argument. It also does not have #classmethod or #staticmethod as decorator. However, it works if called with class:
A.myfn()
Output:
In myfn method.
But give an error if called from any instance:
a = A()
a.myfn()
Error output:
Traceback (most recent call last):
File "testing.py", line 16, in <module>
a.myfn()
TypeError: myfn() takes 0 positional arguments but 1 was given
probably because self was also sent as an argument.
What kind of function will this be called? Will it be a static function? Is it advisable to use function like this in classes? What is the drawback?
Edit: This function works only when called with class and not with object/instance. My main question is what is such a function called?
Edit2: It seems from the answers that this type of function, despite being the simplest form, is not accepted as legal. However, as no serious drawback is mentioned in any of many answers, I find this can be a useful construct, especially to group my own static functions in a class that I can call as needed. I would not need to create any instance of this class. In the least, it saves me from typing #staticmethod every time and makes code look less complex. It also gets derived neatly for someone to extend my class. Although all such functions can be kept at top/global level, keeping them in class is more modular. However, I feel there should be a specific name for such a simple construct which works in this specific way and it should be recognized as legal. It may also help beginners understand why self argument is needed for usual functions in a Python class. This will only add to the simplicity of this great language.
The function type implements the descriptor protocol, which means when you access myfn via the class or an instance of the class, you don't get the actual function back; you get instead the result of that function's __get__ method. That is,
A.myfn == A.myfn.__get__(None, A)
Here, myfn is an instance method, though one that hasn't been defined properly to be used as such. When accessed via the class, though, the return value of __get__ is simply the function object itself, and the function can be called the same as a static method.
Access via an instance results in a different call to __get__. If a is an instance of A, then
a.myfn() == A.myfn.__get__(a, A)
Here , __get__ tries to return, essentially, a partial application of myfn to a, but because myfn doesn't take any arguments, that fails.
You might ask, what is a static method? staticmethod is a type that wraps a function and defines its own __get__ method. That method returns the underlying function whether or not the attribute is accessed via the class or an instance. Otherwise, there is very little difference between a static method and an ordinary function.
This is not a true method. Correctly declarated instance methods should have a self argument (the name is only a convention and can be changed if you want hard to read code), and classmethods and staticmethods should be introduced by their respective decorator.
But at a lower level, def in a class declaration just creates a function and assigns it to a class member. That is exactly what happens here: A.my_fn is a function and can successfully be called as A.my_fn().
But as it was not declared with #staticmethod, it is not a true static method and it cannot be applied on a A instance. Python sees a member of that name that happens to be a function which is neither a static nor a class method, so it prepends the current instance to the list of arguments and tries to execute it.
To answer your exact question, this is not a method but just a function that happens to be assigned to a class member.
Such a function isn't the same as what #staticmethod provides, but is indeed a static method of sorts.
With #staticmethod you can also call the static method on an instance of the class. If A is a class and A.a is a static method, you'll be able to do both A.a() and A().a(). Without this decorator, only the first example will work, because for the second one, as you correctly noticed, "self [will] also [be] sent as an argument":
class A:
#staticmethod
def a():
return 1
Running this:
>>> A.a() # `A` is the class itself
1
>>> A().a() # `A()` is an instance of the class `A`
1
On the other hand:
class B:
def b():
return 2
Now, the second version doesn't work:
>>> B.b()
2
>>> B().b()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: b() takes 0 positional arguments but 1 was given
further to #chepnet's answer, if you define a class whose objects implement the descriptor protocol like:
class Descr:
def __get__(self, obj, type=None):
print('get', obj, type)
def __set__(self, obj, value):
print('set', obj, value)
def __delete__(self, obj):
print('delete', obj)
you can embed an instance of this in a class and invoke various operations on it:
class Foo:
foo = Descr()
Foo.foo
obj = Foo()
obj.foo
which outputs:
get None <class '__main__.Foo'>
get <__main__.Foo object at 0x106d4f9b0> <class '__main__.Foo'>
as functions also implement the descriptor protocol, we can replay this by doing:
def bar():
pass
print(bar)
print(bar.__get__(None, Foo))
print(bar.__get__(obj, Foo))
which outputs:
<function bar at 0x1062da730>
<function bar at 0x1062da730>
<bound method bar of <__main__.Foo object at 0x106d4f9b0>>
hopefully that complements chepnet's answer which I found a little terse/opaque
I am working in a dynamic programming environment where I might need to define (or redefine) a class function. So consider this for example:
def func(self):
print("hello2 \n")
class ManClass:
def __init__(self):
pass
def func1(self):
print("hello1\n")
a = ManClass()
a.func1()
hello1
a.func2 = func
>>> a.func2()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: func() takes exactly 1 argument (0 given)
If func2() had been defined inside the class - a.func2() would have been interpreted as ManClass.func2(a) - but now that I am assigning it outside, it seems to expect an argument. How do I fix this, but more importantly, why this difference in how the two definitions are interpereted ?
You didn't add func to the class, you added it to an instance. Try ManClass.func2 = func instead.
a.func2 = func adds func to the a instance of the class as an instance attribute named func2, not as an instance member method (which is really just special handling for callable members on the underlying class object).
Alternatively, you can also add a member method to a single instance using MethodType, as #jonrsharpe points out in his answer.
This is the difference between a function and a bound method, where "bound" refers to the instance self. To fix your problem, you need to make the standalone function MethodType:
from types import MethodType
a.func2 = MethodType(func, a)
This binds the func to the ManClass instance a, allowing it to access any instance attributes. Note that this only affects a, other ManClass instances will retain the original class definition unless similarly patched.
When you simply attach the function
a.func2 = func
you can still access it:
a.func2(None) # will print "hello2 \n"
But it doesn't get the implicit object instance self parameter and just treats it as a standard positional argument.
Example code:
# -*- coding: utf-8 -*-
from functools import wraps
class MyClass(object):
def __init__(self):
pass
#decorator inside class
def call(f):
#wraps(f)
def wrapper(*args):
print 'Wrapper: ', args
return wrapper
#decorated 'method' without self
#call
def myfunc(a):
pass
c = MyClass()
c.myfunc(1)
Returns:
Wrapper: (<test3.MyClass object at 0xb788a34c>, 1)
Is this normal? Can someone explain?
If this is a feature I would use it in my library.
This is perfectly normal.
The function myfunc is replacecd by an instance of wrapper. The signature of wrapper is (*args). because it is a bound method, the first argument is the instance of MyClass which is printed out after the string `Wrapper: '.
What's confusing you?
It's worth noting that if you use call as a decorator from outside of MyClass, it will generate a TypeError. One way around this is to apply the staticmethod decorator to it but then you can't call it during class construction.
It's a little bit hacky but I address how to have it both ways here.
update after comment
it gets the instance as the first argument regardless of if you type self in the parameter list because after the class is created, and an instance instantiated, it is a bound method. when you call it in the form
#instance.call
def foo(bar):
return bar + 1
it expands to
def foo(bar):
return bar + 1
foo = instance.call(f)
but note that you are calling it on an instance! This will automatically expand to a call of the form
def foo(bar):
return bar + 1
foo = MyClass.call(instance, f)
This is how methods work. But you only defined call to take one argument so this raises a TypeError.
As for calling it during class construction, it works fine. but the function that it returns gets passed an instance of MyClass when it is called for the same reason that I explained above. Specifically, whatever arguments you explicity pass to it come after the implicit and automatic placement of the instance that it is called upon at the front of the argument list.
#call
def myfunc(a):
...
is equivalent to
def myfunc(a):
...
myfunc=call(myfunc)
The orginial myfunc may have expected only one argument, a, but after being decorated with call, the new myfunc can take any number of positional arguments, and they will all be put in args.
Notice also that
def call(f)
never calls f. So the fact that
def myfunc(a)
lacks the normal self argument is not an issue. It just never comes up.
When you call c.myfunc(1), wrapper(*args) gets called.
What is args? Well, since c.myfunc is a method call, c is sent as the first argument, followed by any subsequent arguments. In this case, the subsequent argument is 1. Both arguments are sent to wrapper, so args is the 2-tuple (c,1).
Thus, you get
Wrapper: (<test3.MyClass object at 0xb788a34c>, 1)
It seems that an acceptable answer to the question
What is a method?
is
A method is a function that's a member of a class.
I disagree with this.
class Foo(object):
pass
def func():
pass
Foo.func = func
f = Foo()
print "fine so far"
try:
f.func()
except TypeError:
print "whoops! func must not be a method after all"
Is func a member of Foo?
Is func a method of Foo?
I am well aware that this would work if func had a self argument. That's obvious. I'm interested in if it's a member of foo and in if it's a method as presented.
You're just testing it wrong:
>>> class Foo(object): pass
...
>>> def func(self): pass
...
>>> Foo.func = func
>>> f = Foo()
>>> f.func()
>>>
You error of forgetting to have in the def the self argument has absolutely nothing to do with f.func "not being a method", of course. The peculiar conceit of having the def outside the class instead of inside it (perfectly legal Python of course, but, as I say, peculiar) has nothing to do with the case either: if you forget to have a first argument (conventionally named self) in your def statements for methods, you'll get errors in calling them, of course (a TypeError is what you get, among other cases, whenever the actual arguments specified in the call can't match the formal arguments accepted by the def, of course).
The type error wouldn't be thrown if func had a self argument, like any other instance method.
That's because when you evaluate f.func, you're actually binding f to the first argument of the function -- it then becomes a partial application which you can provide further arguments to.
If you want it to be a static method, then you need the staticmethod decorator which just throws away the first parameter and passes the rest into the original function.
So 2 ways of making it work:
def func(self): pass
-- or --
Foo.func = staticmethod(func)
depending on what you're aiming for.
As written, func is a member of Foo and a method of Foo instances such as your f. However, it can't be successfully called because it doesn't accept at least one argument.
func is in f's namespace and has a _call_() method. In other words, it is enough of a method that Python tries to call it like one when you invoke it like one. If it quacks like a duck, in other words...
That this call doesn't succeed is neither here nor there, in my opinion.
But perhaps a proper response to "But Doctor! When I don't accept any arguments in a function defined in a class, I get confused about whether it's really a method or not!" is simply "Don't do that." :-)