On 08Jan2016 10:19, Steven D'Aprano <st...@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 <c...@zip.com.au>
_______________________________________________
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor

Reply via email to