Classmethods are evil

I

Ivan Illarionov

After re-reading "Python is not Java" I finally came to conclusion that
classmethods in Python are a very Bad Thing.

I can't see any use-case of them that couldn't be re-written more clearly
with methods of metaclass or plain functions.

They have the following issues:
1. You mix instance-level and class-level functionality in one place
making your code a mess.
2. They are slower than metaclass methods or plain functions.

I really want to hear your opinions on the subject.

-- Ivan
 
M

Marc 'BlackJack' Rintsch

After re-reading "Python is not Java" I finally came to conclusion that
classmethods in Python are a very Bad Thing.

I can't see any use-case of them that couldn't be re-written more clearly
with methods of metaclass or plain functions.

*The* use case IMHO are alternative constructors. They belong to the
class, so functions are not as clear and it's possible to have more than
one class in a module with class methods of the same name, e.g.
`A.from_string()` and `B.from_string()` vs. `create_a_from_string()` and
`create_b_from_string()`.

And I don't see how functions can be inherited by sub classes like class
methods can.

Metaclasses are more clear than class methods? You must be joking!?
They have the following issues:
1. You mix instance-level and class-level functionality in one place
making your code a mess.

Writing meta classes just for alternative constructors seems to be more of
a mess to me. Too much magic for such a simple case for my taste.

Ciao,
Marc 'BlackJack' Rintsch
 
C

Carl Banks

After re-reading "Python is not Java" I finally came to conclusion that
classmethods in Python are a very Bad Thing.

I can't see any use-case of them that couldn't be re-written more clearly
with methods of metaclass or plain functions.

I am probably one of the bigger advocates of metaclasses here, but
given a problem that could equally well be solved by the two I would
choose classmethods every time. Classmethods are more
straightforward, easier to follow (especially by people who aren't
used to working with metaclasses), and they hinder reusability less.

Classmethods have practical advantages. One thing you can do with a
classmethod that you can't directly do with a metaclass instancemethod
is to call it with an instance:

Python 2.5 (r25:51908, Apr 19 2007, 15:29:43)
[GCC 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)] on linux2
Type "help", "copyright", "credits" or "license" for more
information. ... def hello(self):
... print "hello"
... ... __metaclass__ = A
... @classmethod
... def world(self):
... print "world"
... Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'B' object has no attribute 'hello'

Though I can imagine some people would argue this is an advantage of
metaclasses.

They have the following issues:
1. You mix instance-level and class-level functionality in one place
making your code a mess.
2. They are slower than metaclass methods or plain functions.

I really want to hear your opinions on the subject.

I think neither of these reasons are very compelling. I don't even
agree with the premise of #1, but even granting that it's messy to do
that, I disagree that the mess would outweigh the drawbacks in
complexity and reusability that a metaclass would bring. As for #2, I
don't believe "class-level functionality" is often found in bottleneck
situations, so what does it matter what speed it is?


Carl Banks
 
G

Gabriel Genellina

En Sat, 17 May 2008 01:01:50 -0300, Ivan Illarionov
After re-reading "Python is not Java" I finally came to conclusion that
classmethods in Python are a very Bad Thing.

I can't see any use-case of them that couldn't be re-written more clearly
with methods of metaclass or plain functions.

A good use case for class methods are alternate constructors, like
dict.from_keys. I don't think an alternate constructor would be more clear
being a method of the metaclass - actually it belongs to the class itself,
not to its metaclass.
Metaclass methods are harder to find; they don't show in dir(instance) nor
dir(class).
Also the resolution order is harder to grasp for metaclasses - but this
may be just lack of usage from my part...
They have the following issues:
1. You mix instance-level and class-level functionality in one place
making your code a mess.

Not necesarily; some classmethods are naturally tied to the class itself,
not to the metaclass (like the constructor example above). But yes, *some*
classmethods could be written as methods of their metaclass instead - but
that doesn't always make sense.
2. They are slower than metaclass methods or plain functions.

Hu? How did you come to that?
I've done a small test and a class method wins by a very minuscule but
consistent advantage over a metaclass method:

class A(object):
color = "red"

@classmethod
def foo(cls, x):
return getattr(cls, x)

class MetaB(type):
def foo(self, x):
return getattr(self, x)

class B(object):
__metaclass__ = MetaB
color = "red"

C:\TEMP>python -m timeit -s "from meta3 import A,B;a,b=A(),B()"
"A.foo('color')"
1000000 loops, best of 3: 1.19 usec per loop

C:\TEMP>python -m timeit -s "from meta3 import A,B;a,b=A(),B()"
"B.foo('color')"
1000000 loops, best of 3: 1.2 usec per loop
 
I

Ivan Illarionov

En Sat, 17 May 2008 01:01:50 -0300, Ivan Illarionov


A good use case for class methods are alternate constructors, like
dict.from_keys. I don't think an alternate constructor would be more
clear being a method of the metaclass - actually it belongs to the class
itself, not to its metaclass.
Metaclass methods are harder to find; they don't show in dir(instance)
nor dir(class).
Also the resolution order is harder to grasp for metaclasses - but this
may be just lack of usage from my part...


Not necesarily; some classmethods are naturally tied to the class
itself, not to the metaclass (like the constructor example above). But
yes, *some* classmethods could be written as methods of their metaclass
instead - but that doesn't always make sense.


Hu? How did you come to that?
I've done a small test and a class method wins by a very minuscule but
consistent advantage over a metaclass method:

class A(object):
color = "red"

@classmethod
def foo(cls, x):
return getattr(cls, x)

class MetaB(type):
def foo(self, x):
return getattr(self, x)

class B(object):
__metaclass__ = MetaB
color = "red"

C:\TEMP>python -m timeit -s "from meta3 import A,B;a,b=A(),B()"
"A.foo('color')"
1000000 loops, best of 3: 1.19 usec per loop

C:\TEMP>python -m timeit -s "from meta3 import A,B;a,b=A(),B()"
"B.foo('color')"
1000000 loops, best of 3: 1.2 usec per loop

How did I come to this:
http://code.djangoproject.com/changeset/7098

I measured this and there was a marginal speed increase when classmethods
wher moved to metaclass.

-- Ivan
 
H

Hans Nowak

Ivan said:
After re-reading "Python is not Java" I finally came to conclusion that
classmethods in Python are a very Bad Thing.

I can't see any use-case of them that couldn't be re-written more clearly
with methods of metaclass or plain functions.

I agree with your sentiments, although I'm not sure I would pick metaclasses
over class methods... or over anything at all, for that matter. :)
They have the following issues:
1. You mix instance-level and class-level functionality in one place
making your code a mess.
2. They are slower than metaclass methods or plain functions.

The way I see it, a class method is really just sugar for a function operating
on the class, living in the class namespace. As such, they are basically
redundant, and as you point out, they can always be replaced by a function
outside the class (and in fact, this was what people did before they came along
in 2.2). Personally, I don't use them... but some people like them. Different
strokes, and all that...

--Hans
 
B

Bruno Desthuilliers

Ivan Illarionov a écrit :
After re-reading "Python is not Java" I finally came to conclusion that
classmethods in Python are a very Bad Thing.
>
I can't see any use-case of them that couldn't be re-written more clearly
with methods of metaclass or plain functions.

Plain functions don't give you polymorphic dispatch and can't be
overriden. Using metaclass methods requires a custom metaclass, which 1/
adds boilerplate code and cognitive load and 2/may bring problems,
specially wrt/ MI.
They have the following issues:
1. You mix instance-level and class-level functionality in one place
making your code a mess.

May I remind you that every name defined at the top-level of a class
statement becomes a class attribute ?

Anyway : most of the use case I've had so far for classmethods required
collaboration between the instance and the class, and I definitively
prefer to have related functionalities defined in one place.
2. They are slower than metaclass methods or plain functions.

Slower than plain functions, indeed - but so are instancemethods. Slower
that metaclass methods ? I never benchmarked this, but I don't see any
reason that should be the case. Anyway, you could also argue that
properties are slower than plain attributes, which are slower than local
vars etc...
I really want to hear your opinions on the subject.


Mine is that classmethods are a GoodThing that make simple thing easy.
 
B

bruno.desthuilliers

How did I come to this:http://code.djangoproject.com/changeset/7098

I measured this and there was a marginal speed increase when classmethods
wher moved to metaclass.

IIRC (please correct me if I'm wrong), this part of code is only
called when the class is created - in which case it makes sense to
move it where it belongs, ie to the metaclass. This is by no mean a
use case for classmethods.
 
R

Raymond Hettinger

After re-reading "Python is not Java" I finally came to conclusion that
classmethods in Python are a very Bad Thing.

Sounds like a serious case of mis-learning.

Class methods are the preferred way to implement alternate
constructors. For good examples, see dict.fromkeys() or any of the
various datetime constructors.

Raymond
 
I

Ivan Illarionov

IIRC (please correct me if I'm wrong), this part of code is only called
when the class is created - in which case it makes sense to move it
where it belongs, ie to the metaclass. This is by no mean a use case for
classmethods.

Yes, this is not the use case for class methods, but they where used
there. The whole point of my post was to say that classmethods are used
in a wrong way too often and most of Python programmers don't know that
the same thing can be implemented with metaclass methods.

-- Ivan
 
B

Bruno Desthuilliers

Ivan Illarionov a écrit :
Yes, this is not the use case for class methods, but they where used
there.

Don't blame the tool for being misused.
The whole point of my post was to say that classmethods are used
in a wrong way too often

I think it's the first time I see such a misuse of classmethods, and I
have read quite a lot of (sometimes pretty hairy) Python code.
and most of Python programmers don't know that
the same thing can be implemented with metaclass methods.

I'd say that most Python programmers using metaclasses know when to use
a metaclass method and when to use a classmethod. One case of (slight)
misuse is certainly not enough to infer a general rule, and that doesn't
make classmethods bad in anyway. And while we're at it, I'd consider
using a custom metaclass only to implement the equivalent of a
classmethod a misuse too - and in this case, kind of a WTF.

As a last point, there's at least one very big difference between a
metaclass method and a classmethod, which is that you cannot call a
metaclass method on an instance:

class MyMeta(type):
def bar(cls):
print "%s.bar()" % cls

class Foo(object):
__metaclass__ = MyMeta

@classmethod
def baaz(cls):
print "%s.baaz()" % cls

Foo.baaz()
Foo.bar()

f = Foo()
f.baaz()
f.bar()

=>

<class '__main__.Foo'>.baaz()
<class '__main__.Foo'>.bar()
<class '__main__.Foo'>.baaz()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/tmp/python-18506gCT.py", line 17, in <module>
f.bar()
AttributeError: 'Foo' object has no attribute 'bar'

I've had use case where I needed to call classmethods on instances,
using a metaclass method would have required an explicit call, ie
type(obj).some_method() instead of obj.some_method(), which would have
uselessly exposed this knowledge to client code.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,431
Messages
2,571,677
Members
48,796
Latest member
Greg L.

Latest Threads

Top