[Tutor] method, type?
Cameron Simpson
cs at zip.com.au
Thu Jan 7 19:54:13 EST 2016
On 08Jan2016 10:19, Steven D'Aprano <steve at pearwood.info> wrote:
>[...]
>> "factory" methods (typically called '.from_*') can be:
Maybe I should have said "often" instead of "typically", if I said "typically".
I think they read well that way and there are several stdlib functions named
this way as a precedent. I'm aiming for the notion "make a Foo from this or
that or something else (3 distinct methods/functions, all taking arguments a
bit different from the core __init__)".
>> 1. a normal function outside the class, or
>> 2. a class method (would allow subclassing.)
>
>"Factory methods" just means a method which you, the creator or author,
>thinks of as a factory. What's a factory? A function or method which
>takes a bunch of arguments and creates something useful.
>
>It's a pretty vague definition, because it's a pretty vague term.
It's mostly vague if you take the "everything is an object in Python" stance.
Which is strictly true, but it is often useful to be thinking of a factory
function as an alternative to the bare ClassName(args-for-__init__)
constructor, where those arguments are not convenient. Such as Alex's
JournalLineItem construction from a line of text.
[...]
>> "alternative constructor" (what Petter Otten and Steven DAprano
>> call it,)
>
>Alternative in the sense of "not the default", that is all.
>
>> would be best placed immediately after __init__.
>
>*shrug*
>It doesn't matter where you put it inside the class. That is entirely a
>matter of personal taste.
I suggested this as a style thing (of course, inherently a matter of personal
taste:-) I like functions with the same purpose to be textually close together.
[...]
>> [Alan Gauld] recommends making it a factory
>> function (defined at the module level, outside the class.)
>
>That's a matter of personal taste, and one which I happen to disagree
>with. Look at the design of the built-in classes like dict. We have
>dict.fromkeys(), not a global fromkeys() function.
I'm largely with Steven here rather than Alan, partly because a classmethod
subclasses nicely (with all the caveats Alan alluded to - if you do this then
your subclasses _may_ need to override the extra constructor just as they may
need to override other methods), and partly because it keeps the constructor
inside the class definition, which I find conceptually tidier.
>> Steven DAprano calls it a Python3 regular function/ a Python2
>> broken method and mentions the Descriptor protocol and how
>> 'methods' are initially simply functions that are then converted
>> to methods (bound) as required. In my case it would be an
>> 'unbound' method which works in 3 but not in Python2.
>>
>> Cameron Simpson indicated that putting @staticmethod above my 'method'
>> would be OK (although not preferred.) Present or absent, my method
>> still functions the same way.
>
>Only because you're just calling it from the class. As soon as you
>create an instance and call the method from that, you'll see why it
>is broken :-)
Aye. While we're on what staticmethod and classmethod accomplish, we could stop
treating them like magic. Have you (alex) written any decorators? They are
functions which accept a function definition and return a wrapper function with
tweaked behaviour. So the notation:
@foo
def func1(blah):
defines "func1" and then calls "foo(func1)". "foo" returns a new function
definition, and the class binds _that_ definition to its "func1" method.
So...
>> The table provided by Peter Otten (very helpful:)
>> -----------------------------------------------------------------
>> invoked with | @staticmethod | @classmethod | no decorator
>> ------------------------------------------------------------------
>> class | args unchanged | class as 1st arg | args unchanged
>> instance | args unchanged | class as 1st arg | inst as 1st arg
>> -------------------------------------------------------------------
>> It suggests that use of the @staticmethod serves to protect one should
>> the 'method' be called via an instance rather than the class. Has it
>> any other effect?
>
>Yes, to confuse people into thinking they should be using staticmethod
>when what they really should use is classmethod :-)
Now consider what @staticmethod achieves: it causes a normal method to be
called as though it were a global function i.e. without the normally implied
"self" parameter. So we could write our own:
def staticmethod(func):
def method(self, *a, **kw):
return func(*a, **kw)
return method
As described above, this effectively installs the "method" function as the
class's actual method, and that function's whole purpose is simply to _discard_
the self parameter and call the original function without "self".
Once that makes sense, you can them imagine writing @classmethod similarly:
def classmethod(func):
def method(self, *a, **kw):
return func(type(self), *a, **kw)
return method
This version discards "self" but passes in its type (== its class).
Now, both of these examples above are actually simplifications of what Python's
inbuilt @staticmethod and @classmethod decorators do but they show that this
isn't magic: it has simple and concrete actions with well defined effects.
Cheers,
Cameron Simpson <cs at zip.com.au>
More information about the Tutor
mailing list