Let us, counterfactually, assume I had a good reason for wanting to make builtin print a static method of some class.
My, apparently wrong, gut feeling was that I need to declare it static doing something like
class sm:
p = staticmethod(print)
as opposed to
class no_sm:
p = print
But it seems both work just fine.
a = sm()
b = no_sm()
a.p("hello")
b.p("hello")
prints
hello
hello
Why does it just work and is there any difference between the two?
Related: Why use staticmethod instead of no decorator at all
for ~most normal functions, they go through a descriptor protocol (__get__) and so they need special decorating when attached to classes in the way you're doing.
take for example:
def f():
print('hi')
class C:
f = f
class D:
f = staticmethod(f)
in this case C().f() errors:
>>> C().f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: f() takes 0 positional arguments but 1 was given
that's because it goes through:
>>> C.f.__get__(C())()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: f() takes 0 positional arguments but 1 was given
but you'll notice, that print has no such attribute __get__:
>>> hasattr(print, '__get__')
it even has a different type!
>>> type(print)
<class 'builtin_function_or_method'>
>>> type(f)
<class 'function'>
so the answer is: print (along with other C functions) without special treatment do not participate in the method descriptor protocol so they act as normal callable objects without attachment of self
Related
This question already has answers here:
Inner class function without self
(5 answers)
Closed 2 years ago.
I was reading about instance, static, and class methods, and found the following.
This:
class A:
def a(self, num):
print("A.a(): ", num)
#staticmethod
def aa(num):
print("A.aa(): ", num)
works exactly as expected:
>>> A.a(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: a() missing 1 required positional argument: 'num'
# I understand that this happens since `a()` is an instance method
>>> A.aa(1)
A.aa(): 1
>>> A().a(1)
A.a(): 1
However, if I modify A.a() to remove self from its parameters, i.e.:
class A:
def a(num):
print("A.a(): ", num)
#staticmethod
def aa(num):
print("A.aa(): ", num)
this happens:
>>> A.a(1)
A.a(): 1
# I don't understand why this works
>>> A.aa(1)
A.aa(): 1
>>> A().a(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: a() takes 1 positional argument but 2 were given
# I understand that this happens because the first argument
# being given to `a()` is the instance of A
What exactly happened here? If I don't pass self to the a() method, what kind of a method is it? Why does it work without an instance of the class?
This is a static method. It will work, but it will not be able to access any of the other properties/methods that the class has. For example, suppose you have the following class.
class newClass:
b = 9
def print_b():
print(b)
newClass.print_b()
This will throw an error, since the function can not access the variable b. Hope this helps.
Similarly, you can not do the following, because when you call a method like this, you automatically pass an instance of the class into the function. Consequently, the function will throw an error saying it expects 0 positional arguments, but you have passed one.
x = newClass()
x.print_b()
I'm trying to reduce copy/paste in my code and have stumbled upon this problem. I've googled for the answer but all answers use an instance of a class as the key, I can't find anything on using a class definition itself as the key (I don't know if it's possible).
My code is this:
# All chunkFuncs keys are class definitions, all values are functions
chunkFuncs = {Math_EXP : Math_EXPChunk, Assignment : AssignmentChunk, Function : FunctionChunk}
def Chunker(chunk, localScope):
for chunkType in chunkFuncs:
if isinstance(chunk,chunkType):
# The next line is where the error is raised
localScope = chunkFuncs[chunk](chunk,localScope)
return localScope
and the error is this
TypeError: unhashable type: 'Assignment'
Here are the class definitions:
class Math_EXP(pyPeg.List):
grammar = [Number,Symbol],pyPeg.maybe_some(Math_OP,[Number,Symbol])
class Assignment(pyPeg.List):
grammar = Symbol,'=',[Math_EXP,Number]
class Function(pyPeg.List):
grammar = Symbol,'(',pyPeg.optional(pyPeg.csl([Symbol,Number])),')'
Are there any alternative methods I could use to get the same effect?
Thanks.
OK, the comments are getting out of hand ;-)
It seems certain now that the class object isn't the problem. If it were, the error would have triggered on the first line, when the dict was first constructed:
chunkFuncs = {Math_EXP : Math_EXPChunk, Assignment : AssignmentChunk, Function : FunctionChunk}
If you try to construct a dict with an unhashable key, the dict creation fails at once:
>>> {[]: 3}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
But you got beyond that line, and Assignment is a key in the dict you constructed. So the error is in this line:
localScope = chunkFuncs[chunk](chunk,localScope)
Best guess is that it's an instance of Assignment that's unhashable:
>>> class mylist(list):
... pass
...
>>> hash(mylist)
2582159
>>> hash(mylist())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'mylist'
See? mylist is hashable, but the instance mylist() is not.
Later: best guess is that you're not going to be able to worm around this. Why? Because of the name of the base class, pyPeg.List. If it's mutable like a Python list, then instances won't be hashable - and shouldn't be (mutable objects are always dangerous as dict keys). You could still index a dict by id(the_instance), but whether that's semantically correct is something I can't guess without knowing a lot more about your code.
You should be able to, yes, but you might need an extra type call:
>>> class X:
... pass
...
>>> class_map = {X: 5}
>>> my_x = X()
>>> class_map[type(my_x)]
5
There are several good explanations on SO about why/when you should use a class method vs a static method, but I've not been able to find an answer for when you would use a static method over no decoration at all. Consider this
class Foo(object):
#staticmethod
def f_static(x):
print("static version of f, x={0}".format(x))
def f_standalone(x):
print("standalone verion of f, x={0}".format(x))
And some output:
>>> F = Foo
>>> f = F()
>>> F.f_static(5)
static version of f, x=5
>>> F.f_standalone(5)
standalone verion of f, x=5
>>> f.f_static(5)
static version of f, x=5
>>> f.f_standalone(5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: f_standalone() takes 1 positional argument but 2 were given
From what I've read on here, the primary reason for using staticmethod is basically to keep conceptually similar things together. From the example above, it seems like both solutions do that. The only drawback is that you don't appear to be able to call the non-staticmethod from an instance. Maybe I'm too used to other programming languages, but this does not bother me so much; it's always surprising that I can call class-level stuff from am instance in Python.
So, is this basically the only difference between the two? Or am I missing other benefits? Thanks
You seem to be using python 3. In python 2:
In [1]: class Foo(object):
...: def f_standalone(x):
...: print("standalone version of f, x={}".format(x))
...:
In [2]: Foo.f_standalone(12)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-2-2d315c006153> in <module>()
----> 1 Foo.f_standalone(12)
TypeError: unbound method f_standalone() must be called with Foo instance as first argument (got int instance instead)
In python 3, you missed another strange use case:
In [1]: class Foo(object):
...: def f_standalone(x):
...: print("standalone version of f, x={}".format(x))
...: #staticmethod
...: def f_static(x):
...: print("static version of f, x={}".format(x))
...:
In [2]: Foo().f_standalone()
standalone version of f, x=<__main__.Foo object at 0x1064daa10>
In [3]: Foo().f_static()
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-3-addf9c3f2477> in <module>()
----> 1 Foo().f_static()
TypeError: f_static() missing 1 required positional argument: 'x'
Is there a difference between the two methods?
For example,
from datetime import date
today = date(2012, 10, 13)
repr(today)
'datetime.date(2012, 10, 13);
today.__repr__()
'datetime.date(2012, 10, 13)'
They seem to do the same thing, but why would someone want to use the latter over the regular repr?
__repr__ method is used to implement custom result for repr(). It is used by repr(), str() (if __str__ is not defined). You shouldn't call __repr__ explicitly.
The difference is that repr() enforces the string as the returned type and repr() looks up __repr__ on a class object, not an instance itself:
>>>> class C(object):
.... def __repr__(self):
.... return 1 # invalid non-string value
....
>>>> c = C()
>>>> c.__repr__() # works
1
>>>> repr(c) # enforces the rule
Traceback (most recent call last):
File "<console>", line 1, in <module>
TypeError: __repr__ returned non-repr (type 'int')
>>>> c # calls repr() implicitly
Traceback (most recent call last):
File "<console>", line 1, in <module>
TypeError: __repr__ returned non-repr (type 'int')
>>>> str(c) # also uses __repr__
Traceback (most recent call last):
File "<console>", line 1, in <module>
TypeError: __str__ returned non-str (type 'int')
>>>> c.__repr__ = lambda: "a"
>>>> c.__repr__() # lookup on instance
'a'
>>>> repr(c) # old method from the class
Traceback (most recent call last):
File "<console>", line 1, in <module>
TypeError: __repr__ returned non-repr (type 'int')
>>>>
It's the same thing
Think of repr() as containing the following code:
def repr(obj):
return obj.__repr__()
All it does is call the object's __repr__() function. I'm not sure why anyone would need to call the object's __repr__() method explicitly. In fact, it's generally bad coding style to do so (it's confusing, and leads the programmer to ask questions like the one that you did just now).
It's not a real world program but I would like to know why it can't be done.
I was thinking about numpy.r_ object and tried to do something similar but just making a class and not instantiating it.
a simple code (has some flaws) for integers could be:
class r_:
#classmethod
def __getitem__(clc, sl):
try:
return range(sl)
except TypeError:
sl = sl.start, sl.stop, sl.step
return range(*(i for i in sl if i is not None))
but as I try to do r_[1:10] i receive TypeError: 'type' object is not subscriptable.
Of course the code works with r_.__getitem__(slice(1,10)) but that's not what I want.
Is there something I can do in this case instead of using r_()[1:10]?
The protocol for resolving obj[index] is to look for a __getitem__ method in the type of obj, not to directly look up a method on obj (which would normally fall back to looking up a method on the type if obj didn't have an instance attribute with the name __getitem__).
This can be easily verified.
>>> class Foo(object):
pass
>>> def __getitem__(self, index):
return index
>>> f = Foo()
>>> f.__getitem__ = __getitem__
>>> f[3]
Traceback (most recent call last):
File "<pyshell#8>", line 1, in <module>
f[3]
TypeError: 'Foo' object does not support indexing
>>> Foo.__getitem__ = __getitem__
>>> f[3]
3
I don't know why exactly it works this way, but I would guess that at least part of the reason is exactly to prevent what you're trying to do; it would be surprising if every class that defined __getitem__ so that its instances were indexable accidentally gained the ability to be indexed itself. In the overwhelming majority of cases, code that tries to index a class will be a bug, so if the __getitem__ method happened to be able to return something, it would be bad if that didn't get caught.
Why don't you just call the class something else, and bind an instance of it to the name r_? Then you'd be able to do r_[1:10].
What you are trying to do is like list[1:5] or set[1:5] =) The special __getitem__ method only works on instances.
What one would normally do is just create a single ("singleton") instance of the class:
class r_class(object):
...
r_ = r_class()
Now you can do:
r_[1:5]
You can also use metaclasses, but that may be more than is necessary.
"No, my question was about getitem in the class, not in the instance"
Then you do need metaclasses.
class r_meta(type):
def __getitem__(cls, key):
return range(key)
class r_(object, metaclass=r_meta):
pass
Demo:
>>> r_[5]
range(0, 5)
If you pass in r_[1:5] you will get a slice object. Do help(slice) for more info; you can access values like key.stop if isinstance(key,slice) else key.
Define __getitem__() as a normal method in r_'s metaclass.
The reason for this behavior lies in the way how special methods like __getitem__() are lookup up.
Attributes are looked up first in the objects __dict__, and, if not found there, in the class __dict__. That's why e.g. this works:
>>> class Test1(object):
... x = 'hello'
...
>>> t = Test1()
>>> t.__dict__
{}
>>> t.x
'hello'
Methods that are defined in the class body are stored in the class __dict__:
>>> class Test2(object):
... def foo(self):
... print 'hello'
...
>>> t = Test2()
>>> t.foo()
hello
>>> Test2.foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unbound method foo() must be called with Test2 instance as first argument (got nothing
instead)
So far there's nothing surprising here. When it comes to special methods however, Python's behavior is a little (or very) different:
>>> class Test3(object):
... def __getitem__(self, key):
... return 1
...
>>> t = Test3()
>>> t.__getitem__('a key')
1
>>> Test3['a key']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'type' object is unsubscriptable
The error messages are very different. With Test2, Python complains about an unbound method call, whereas with Test3 it complains about the unsubscriptability.
If you try to invoke a special method - by way of using it's associated operator - on an object, Python doesn't try to find it in the objects __dict__ but goes straight to the __dict__ of the object's class, which, if the object is itself a class, is a metaclass. So that's where you have to define it:
>>> class Test4(object):
... class __metaclass__(type):
... def __getitem__(cls, key):
... return 1
...
>>> Test4['a key']
1
There's no other way. To quote PEP20: There should be one-- and preferably only one --obvious way to do it.