I have a python file called sample.py with a class definition of Sample object in. This object has various variables and the following function:
def ratioDivision(numerator, denominator):
Then, in my main function (in another file), I declare a Sample object x, and attempt to call this function:
x.co2overco = x.ratioDivision(float(x.co2), float(x.co))
However, I get this error:
Traceback (most recent call last):
File "csvReader.py", line 192, in <module>
main(sys.argv[1:])
File "csvReader.py", line 79, in main
x.co2overco = x.ratioDivision(float(x.co2), float(x.co))
TypeError: ratioDivision() takes exactly 2 arguments (3 given)
I can't see how I gave three arguments? Is there a issue with the referencing?
Your method an instance method. It's first parameter should be self
def ratioDivision(self, numerator, denominator):
It sees 3 parameters, because the first parameter is the instance itself.
When an attribute lookup (ie obj.name) references a function that's an attribute of the class, then the attribute resolution mechanism yields a callable method object instead of the function. This method object is a wrapper around the function and instance, and when called it injects the instance as first argument, so in your case
x.ratioDivision(1, 2)
becomes
Sample.__dict__["ratioDivision"](x, 1, 2)
If ratioDivision doesn't need any access to the current instance nor class, you could just make it a plain function in your module (Python is not Java and doesn't require that everything lives in a class).
If you still want it to be accessible thru Sample instances (to support for class-based polymorphic dispatch or just for mere practical reasons - like not having to import both Sample and ratioDivision from your module), you can also make it a staticmethod:
class Sample(object):
#staticmethod
def ratioDivision(numerator, denominator):
return whatever
This being said, given your example use case, ie:
x.co2overco = x.ratioDivision(float(x.co2), float(x.co))
you may want to add a method to your Sample class, something like computeCo2overco() :
class Sample(object):
#staticmethod
def ratioDivision(numerator, denominator):
return whatever
def computeCo2overco(self)
self.co2overco = self.ratioDivision(float(self.co2), float(self.co))
or if ratioDivision is not expensive, just use a computed attribute:
class Sample(object):
#staticmethod
def ratioDivision(numerator, denominator):
return whatever
#property
def co2overco(self):
return self.ratioDivision(float(self.co2), float(self.co))
In which case you can just use:
whatever = x.co2coverco + something
and under the hood, it will call the co2overco() function.
Related
I am trying to create a class which gets given a function, which will then be run from that instance. However, when I tried to use staticmethod, I discovered that there is a difference between using the decorator and just passing staticmethod a function.
class WithDec():
def __init__(self):
pass
#staticmethod
def stat(val):
return val + 1
def OuterStat(val):
return val + 1
class WithoutDec():
def __init__(self, stat):
self.stat = staticmethod(stat)
With these two classes, the following occurs.
>>> WithDec().stat(2)
3
>>> WithoutDec(OuterStat).stat(2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'staticmethod' object is not callable
What is going on, and what can I do to stop it.
Static methods still work through the descriptor protocol, meaning that when it is a class attribute, accessing it via an instance still means that the __get__ method will be called to return an object that actually gets called. That is,
WithDec().stat(2)
is equivalent to
w = WithDec()
w.stat(2)
which is equivalent to
WithDec.stat.__get__(w, WithDec)(2)
However, the descriptor protocol is not invoked when the static method is an instance attribute, as is the case with WithoutDec. In that case
WithoutDec().stat(2)
tries to call the literal staticmethod instance stat, not the the function returned by stat.__get__.
What you wanted was to use staticmethod to create a class attribute, just not via decorator syntax:
class WithoutDec():
def stat(val):
return val + 1
stat = staticmethod(stat)
You first bind stat to a regular function (it's not really an instance method until you try to use it as an instance method), then replace the function with a staticmethod instance wrapping the original function.
The problem is that you are trying to use staticmethod() inside __init__, which is used to create an instance of the class, instead of at the class level directly, which defines the class, its methods and its static methods.
This code works:
def OuterStat(val):
return val + 1
class WithoutDec():
stat = staticmethod(OuterStat)
>>> WithoutDec.stat(2)
3
Note that trying to create an instance of WithoutDec with its own, different, version of stat, is contrary to the meaning of a method being static.
I found a very inspiring solution on this thread. Indeed your code is not very pythonic, and attributes a static method to an attribute of an instance of your class. The following code works:
class WithoutDec():
stat = None
#staticmethod
def OuterStat(val):
return val + 1
then you call:
my_without_dec = WithoutDec()
my_without_dec.stat = WithotuDec.OuterStat
my_without_dec.stat(2)
later if you want to create a new method, you call:
def new_func(val):
return val+1
WithoutDec.newStat = staticmethod(new_func)
my_without_dec.stat = WithoutDec.newStat
my_without_dec.stat(2)
Yes -
In this case, you just have to add the function as an attribute of the instance, it will work as expected, no need for any decorators:
def OuterStat(val):
return val + 1
class WithoutDec():
def __init__(self, stat):
self.stat = stat
The thing is: there is a difference if a function is an attribute of the class or an attribute of the instance. When it is set inside an instance method with self.func = X, it becomes an instance attribute - Python retrieves it the way it was stored, with no modifications, and it is simply another reference to the original function that can be called.
When a function is stored as a class attibute, instead, the default behavior is that it is used as an instance method: upon retrieving the function from an instance, Python arranges things so that self will be injected as the first argument to that function. In this case, the decorators #classmethod and #staticmethod exist to modify this behavior (injetct the class for classmethod or make no injection for staticmethod).
The thing is that staticmethod does not return a function - it returns a descriptor to be used as a class attribute, so that when the decorated function is retrieved from a class, it works as a plain function.
(Internal detail: all 3 behaviors: instance method, classmethod and staticmethod are implementing by having an appropriate __get__ method on the object that is used as an attribute to the class).
NB: There were some discussions in making "staticmethod" to become itself "callable", and simply call the wrapped function - I just checked it made it into Pythonn 3.10 beta 1. This means that your example code will work as is for Python 3.10 - nonetheless, the staticmethod call there is redundant, as stated in the beggining of this answer, and should not be used.
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 newcomer to Python language and I have question about inheritance.
Suppose this is my code:
class LogicGate(object):
def __init__(self, n):
self.label = n
self.output = None
def get_label(self):
return self.label
def get_output(self):
self.output = self.perform_gate_logic()
return self.output
class BinaryGate(LogicGate):
def __init__(self, n):
LogicGate.__init__(self, n)
self.pinA = None
self.pinB = None
This is how I see implementation of python object system. Object is just a bunch of variable, methods lives somewhere else. In order for methods to to do some work on particular object I pass 'self' in every method. (if this totally not true, please correct me)
But, I don't understand this:
LogicGate.__init__(self, n)
could you explain what's going here? I'm using python 2.7
All python methods are in fact "just functions". Even special methods, like __init__ are actually just a normal function, that accepts two parameters - self (which could actualy have any name - self is only a convention) and n.
When you create an instance, python creates the instance and passes it as self automaticaly to __init__ method. So, for example you can create LogicGate instance manually:
>>> x = object.__new__(LogicGate)
>>> x
<__main__.LogicGate object at 0x7f12b2ea49d0>
# This won't work, because x instance is not initialized
>>> x.label
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'LogicGate' object has no attribute 'label'
>>> LogicGate.__init__(x, 'foo')
>>> x.label
'foo'
When you want to do initialization from parent class, you must call its __init__ - which is exaclty what LogicGate.__init__(self, n) actually does. It just calls __init__ method of LogicGate class with your instance as the first parameter.
Edit: you should probably use method resolution order, instead of directly calling parent's __init__, i.e.:
super(BinaryGate, self).__init__(n)
This just make sure it will call prober parent class, so if you change it to, something like SuperLogicGate, you won't have to change the call to parent's __init__ too.
You should probably also read this: https://docs.python.org/2/tutorial/classes.html?highlight=class%20inheritance
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.
I'm a new Python programmer who is having a little trouble using 'self' in classes. For example:
class data:
def __init__(self):
self.table = []
def add(self, file):
self.table.append(file)
data.add('yes')
In this function I want to have table be a variable stored in the class data and use add to modify it. However, when I run this script it gives me the error:
Traceback (most recent call last):
File "/Projects/Python/sfdfs.py", line 7, in <module>
data.add('yes')
TypeError: add() takes exactly 2 positional arguments (1 given)
I assume that I am trying to call the function the wrong way in this instance, as this syntax is very similar to an example in the python documentation: http://docs.python.org/3.1/tutorial/classes.html
You first need to make an instance of the class:
mydata = data()
then you can call the method -- on the instance, of course, not on the class:
mydata.add('yes')
You need to instantiate the class before you can call methods on it:
mydata = Data()
mydata.add('yes')
you are calling the add method on the class object not an instance of the class.
It looks like what you want to do is:
classInst = data() #make an instance
classInst.add("stuff") #call the method
When add is invoked on an instance object, the instance object is passed as the self argument to the method. Having the self argument differentiates class methods from instance methods.
You are trying to call data.add() somewhat like you would call a static method in Java.
Try doing this instead:
d = data()
d.add('yes')
The self parameter tells the method that it operates on an object of type data.