Can we call a class method without self? - python

class abc:
def yo(var):
a=var
print(a)
x=abc().yo(5)
Output:
Traceback (most recent call last):
File "main.py", line 5, in
x=abc().yo(5)
TypeError: yo() takes 1 positional argument but 2 were given
class abc:
def yo(self,var):
self.a=var
print(self.a)
x=abc().yo(5)
Output: 5
it's working till i use the self keyword i mean can we call a function without using self parameter in it why it says yo() takes 1 arguments and is given 2 when we exclude self?

you have to use decorators to change the first argument in your class methods ,if you don't put any decorator the fuction have the self argument by defaulf (the instance which use this method), if you put the #classmethod decorator like bellow the first argument is cls (the method's class):
class abc:
a = 'abc'
#classmethod
def aFunction(cls, value)
print(cls.a + value)
the second decorator, probably the one you are looking for allows you to have a class method without default argument:
class abc:
def __init__(self,value):
self.value = value
#staticmethod
def aFunction(value):
return value+'abc'

self is actually a convention used by since a long time and not a real python keyword.
self is nothing but a parameter in function and you can easily use another parameter name in place of it.
Then why use self?
Because it is advisable as it increases the readability of code.
Moreover, if the method does not require self, it usually signifies that it should be a static method

Related

Python time.time accept self args

According to the following snippet:
import time
def custom_time():
return time.time()
class TimeWrapper:
builtin_time = time.time
def print_builtin(self):
print(self.builtin_time())
custom_time = custom_time
def print_custom(self):
print(self.custom_time())
wrapper = TimeWrapper()
wrapper.print_builtin()
# 1660163626.7973292
wrapper.print_custom()
# TypeError: custom_time() takes 0 positional arguments but 1 was given
time.time(wrapper)
# TypeError: time.time() takes no arguments (1 given)
custom_time(wrapper)
# TypeError: custom_time() takes 0 positional arguments but 1 was given
I do not understand why wrapper.print_builtin() is working.
Is it not supposed to be the equivalent of time.time(wrapper)?
Is there a connection with the unused argument from C implementation ?
If it is not the case, I'm still interested in this unused variable.
time.time has type builtin_function_or_method, not function. Such objects do not implement the descriptor protocol:
>>> time.time.__get__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'builtin_function_or_method' object has no attribute '__get__'
so self.builtin_time simply returns time.time, not a method object that implicitly passes self as the first argument to time.time.
That is, self.builtin_time() is exactly the same as time.time(), not time.time(self).
This is an interesting side-effect of how methods really work. A method is just a function in a class dictionary. For example:
class A:
def b(self):
print("b")
def c(self):
print("c")
A.c = c
a = A()
The reason that both b and c are equally methods of A is that they are descriptors. When you access a descriptor from an instance of a class using dot notation, as in a.b or a.c, python will perform the following binding: type(a).b.__get__(a). The bound method is a callable that automatically passes self to the underlying function. Whether you define a method in the class or outside a class and add it to the class dictionary by other means, it will behave the same.
The behavior of c in the example above is exactly the same as custom_time in your example. By setting custom_time = custom_time in the class body, you are creating a method just as if you had placed def custom_time(): in the class body. When you call the subsequently bound method as self.custom_time(), the bound method passes instance self as the first argument, even though the function accepts no arguments. Hence your error.

Class variable reference to function changes to instancemethod

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!

Unable to call function with self

I'm writing a simple code snippet here, but unable to run the code
class test:
def __init__(self):
pass
def functiona(self, a):
b = a+0
print(b)
def functionb(self):
a = 5
self.functiona(a)
test.functionb('abc')
It errors out with "AttributeError: 'str' object has no attribute 'functiona'" Unable to call it with self. However, if I provide test.functiona(a) it works fine.
Few of other code samples works with self.function, how to solve this issue
test.functionb('abc') is a function call on the class, not on an instance.
I suppose it works if you do test().functionb('abc')?
The difference is:
In your case, you call the function object on the class. As it is not a staticmethod or classmethod, it is called with self = 'abc', a string. This string hasn't a method functiona().
In my case, the call operates on a class instance. Here, self is set to the instance you just created – and you get an error because it doesn't know where to pass the 'abc'.
Problem lies in the call test.functionb('abc'). You are not using object of the class to call the method. So, the self parameter is not passed.
Python considers, the first parameter to be self, and you passed 'abc' which is a string.
Use it like test().functionb('abc') , then the default first argument becomes the object of test - like functionb(test_ob, 'abc').
you can add the decorator #classmethod and then call it like you did
class test:
def __init__(self):
pass
#classmethod
def functiona(self, a):
b = a+0
print(b)
#classmethod
def functionb(self):
a = 5
self.functiona(a)
>>> test.functiona(1001)
>>> 1001
>>> test.functionb()
>>> 5
Problem lies in the call test.functionb('abc'). You are not using object of the class to call the method. So, the self parameter is not passed. Python considers, the first parameter to be self, and you passed 'abc' which is a string.
Use it like test().functionb('abc') , then the default first argument becomes the object of test - like functionb(test_ob, 'abc').

Method arguments in Python [duplicate]

This question already has answers here:
What is the purpose of the `self` parameter? Why is it needed?
(26 answers)
Closed 6 months ago.
Suppose I have this code:
class Num:
def __init__(self,num):
self.n = num
def getn(self):
return self.n
def getone():
return 1
myObj = Num(3)
print(myObj.getn()) # result: 3
But if I try print(myObj.getone()), I get an error: 'getone()' takes no arguments (1 given).
So I replace:
def getone():
return 1
with
def getone(self):
return 1
Now print(myObj.getone()) shows 1, as expected. But - getone() doesn't need any arguments in order to just return 1. Do I have to use a meaningless argument?
In Python:
Instance methods: require the self argument.
Class methods: take the class as a first argument.
Static methods: do not require either the instance (self) or the class (cls) argument.
__init__ is a special function and without overriding __new__ it will always be given the instance of the class as its first argument.
An example using the builtin classmethod and staticmethod decorators:
import sys
class Num:
max = sys.maxint
def __init__(self,num):
self.n = num
def getn(self):
return self.n
#staticmethod
def getone():
return 1
#classmethod
def getmax(cls):
return cls.max
myObj = Num(3)
# with the appropriate decorator these should work fine
myObj.getone()
myObj.getmax()
myObj.getn()
That said, I would try to use #classmethod/#staticmethod sparingly. If you find yourself creating objects that consist of nothing but staticmethods the more pythonic thing to do would be to create a new module of related functions.
Every method needs to accept one argument: The instance itself (or the class if it is a static method).
Read more about classes in Python.
The fact that your method does not use the self argument (which is a reference to the instance that the method is attached to) doesn't mean you can leave it out. It always has to be there, because Python is always going to try to pass it in.
In python you must always pass in at least one argument to class methods, the argument is self and it is not meaningless its a reference to the instance itself
The current object is explicitly passed to the method as the first parameter. self is the conventional name. You can call it anything you want but it is strongly advised that you stick with this convention to avoid confusion.
If you print(type(Num.getone)) you will get <class 'function'>.
It is just a plain function, and be called as usual (with no arguments):
Num.getone() # returns 1 as expected
but if you print print(type(myObj.getone)) you will get <class 'method'>.
So when you call getone() from an instance of the class, Python automatically "transforms" the function defined in a class into a method.
An instance method requires the first argument to be the instance object. You can think myObj.getone() as syntactic sugar for
Num.getone(myObj) # this explains the Error 'getone()' takes no arguments (1 given).
For example:
class Num:
def __init__(self,num):
self.n = num
def getid(self):
return id(self)
myObj=Num(3)
Now if you
print(id(myObj) == myObj.getid())
# returns True
As you can see self and myObj are the same object

decorator inside class & decorated classmethod without 'self' gives strange results

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)

Categories