decorat{or,ion}

dieter dieter at handshake.de
Mon May 21 03:11:02 EDT 2018


Mike McClain <mike.junk.46 at att.net> writes:
> ...
>         Thanks for the response, this is still a foreign language to me and I need all
>     the help I can get. I'm reading the docs, doing the tutorial again but still have
>     more questions than answers.
>         If I understand what you said, 'taint necessarily so, I'll restate it in psuedo
>     code since I've little feel for python syntax yet.
>
>     Let's forget buz().
>
>     def bar(*args):
>         (a,b,rest) = parseArgs(args)
>         def baz(x):
>             ...
>         def bug(k,l,m):
>             ...
>         bug(foo(a), baz(b), rest)
>
>     In 'def bar()', baz & bug are simply functions.

Indeed.

>     Are they accessable outside bar?

No, unless they are explicitly passed out by "bar" (usually either as
function result or part thereof; but there are other ways as well).


>     class bar(*args):
>         (a,b,rest) = parseArgs(args)

I do not think that this would work.

While the "class" statement (defining a class)
has some similarity with the "def" statement (defining a function),
there are also differences.

In particular, the arguments in "class C(<arguments>)"
correspond to arguments passed to a function call, not the arguments
of a function definition. As one consequence, they are not made
visible in the class' namespace: your "parseArgs(args)" above
will not work (likely, you will already get an error before that point.

As a side note:
Associated with a class definition is a so called "metaclass"
(the association is usually implicit, but can be made explicit).
The arguments in "class C(<arguments>)" are passed as
one of the parameters in a metaclass call (other parameters are
the class name and the class dict). Thus, the "class" statement
effectively leads to a (function/method) call with the somewhat
prepared content of the class statement as parameters.

>         def baz(x):
>             ...
>         ...
>
>     In 'class bar()' I understand baz and bug should be named _baz_ , _bug_,
>     if they're not expected to be called from outside bar but there's nothing
>     to prevent one from doing so except manners.

It is a tacit convention that names starting with a "_" are in
various ways special:

        __...__: are usually used for Python internal purposes and
          often treated very differently from other attributes.

          Usually, you should avoid to define such attributes
          yourself, unless you want to customize the
          associated Python behaviour.
          Otherwise, future Python versions might give a special
          meaning to your attribute and it may then no longer
          work as in previous Python versions.

        __...: those attributes are mangled inside a class statement:
          the mangling consists of prepending "_<class name>".

          The purpose of this feature is to avoid name clashes
          in class inheritance. "__..." attribute are in some
          way "private" to the class (as far as the mangling
          ensures this), not (easily) seen be deriving classes.

       _...: provides a hint that this attribute/name is ment more for
          "local" rather then "public" use.

          There is nothing in Python that enforces this.


>     Also they're now methods while
>     still being functions and had to be declared 'def baz(self,x):'.

The precise details are as follows:

  The "def" always defines a function (whether or not it appears
  inside or outside of a "class" statement).

  If used inside a "class" statement, the function is put into
  the dictionary associated with the constructed class.

  If an attribute of a class or class instance is looked up
  in the "normal" way" (i.e. "<obj>.<attr>") and
  the result happens to be a function, then the function
  is not returned directly but wrapped into a method wrapper
  (containing both "<obj>" and the function).
  This means that the function is turned into a method
  during its access (not during its definition).

Usually, you access the functions in a "class" definition in the "normal
way" - and the result will behave as a method. Thus, you
can view (for simplicity) the corresponding definitions as method definitions
-- even though the transformation into a method only happens
on access to (and not on definition of) the function.


>     Feel free to laugh if what I'm saying is nonsense in python.

Do not be worried.

>         If I execute 'bar.baz(mu)',

As mentioned above, your example will not work. Let's modify
a bit:

  class C:
    def baz(self, b):
      ...

  c = C()

If you use "C.baz", the result is in Python 2
a so called "unbound method":
it contains the class "C" and the function "baz".
If you want to call an "unbound method", you must
pass in all function arguments, including the first one. The call
will raise a "TypeError", when the first argument is not an instance
of the associated class.
In Python 3, "C.baz" returns the function "baz".

If you use "c.baz", the result is a so called "bound method":
it contains the object "c" and the function "baz".
If you call a "bound method", the associated object
is passed autemoatically as first parameter to the function; you
specify only the remaining arguments in the call.

>     assuming mu is enough like b above for
>     baz not to throw an exception, can I expect the action of baz to change in a
>     manner similar to the change in parameters? Or does something in self prevent that?

I do not understand your question and expectations.

Hopefully, my explanation above will already clarify
Python's treatment of functions versus methods sufficiently,
that you can understand what is happening or at least that
you can reformulate your question.


As a side note: you can fairly safely play with Python
in an interactive way - and thus learn much about Python internals.

Below is a transcript for the exploration of the function/method
handling discussed in the message:

lsdm: python3
Python 3.5.2 (default, Nov 23 2017, 16:37:01) 
[GCC 5.4.0 20160609] on linux
>>> class C:
...   def baz(self, b): print(b)
... 
>>> c = C()
>>> C.baz
<function C.baz at 0xb712c7c4>
>>> C.baz(1,2)
2
>>> c.baz
<bound method C.baz of <__main__.C object at 0xb70311ac>>
>>> c.baz(2)
2
# provide information about the method's attributes
>>> dir(c.baz)
['__call__', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__func__', '__ge__', '__get__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
# "<method>.__self__" contains the associated object
>>> c.baz.__self__ is c
True
# "<method>.__func__" contains the associated function
>>> c.baz.__func__ is C.baz
True




More information about the Python-list mailing list