Python and reference passing. Limitation?

后端 未结 4 1109
隐瞒了意图╮
隐瞒了意图╮ 2020-12-18 09:17

I would like to do something like the following:

class Foo(object):
    def __init__(self):
        self.member = 10
        pass

def factory(foo):
    foo          


        
相关标签:
4条回答
  • 2020-12-18 09:56

    You are making a mistake here because in Python

    "We call the argument passing technique _call by sharing_,
    because the argument objects are shared between the
    caller and the called routine.  This technique does not
    correspond to most traditional argument passing techniques
    (it is similar to argument passing in LISP).  In particular it
    is not call by value because mutations of arguments per-
    formed by the called routine will be visible to the caller.
    And it is not call by reference because access is not given
    to the variables of the caller, but merely to certain objects."
    

    in Python, the variables in the formal argument list are bound to the actual argument objects. the objects are shared between caller and callee; there are no "fresh locations" or extra "stores" involved.

    (which, of course, is why the CLU folks called this mechanism "call- by-sharing".)

    and btw, Python functions doesn't run in an extended environment, either. function bodies have very limited access to the surrounding environment.

    0 讨论(0)
  • 2020-12-18 10:00

    Your factory method doesn't return anything - and by default it will have a return value of None. You assign aTestFoo to None, but never re-assign it - which is where your actual error is coming from.

    Fixing these issues:

    class Foo(object):
        def __init__(self):
            self.member = 10
            pass
    
    def factory(obj):
        return obj()
    
    aTestFoo = factory(Foo)
    print aTestFoo.member
    

    This should do what I think you are after, although such patterns are not that typical in Python (ie, factory methods).

    0 讨论(0)
  • 2020-12-18 10:06

    Python does not have pass by reference. One of the few things it shares with Java, by the way. Some people describe argument passing in Python as call by value (and define the values as references, where reference means not what it means in C++), some people describe it as pass by reference with reasoning I find quite questionable (they re-define it to use to what Python calls "reference", and end up with something which has nothing to do with what has been known as pass by reference for decades), others go for terms which are not as widely used and abused (popular examples are "{pass,call} by {object,sharing}"). See Call By Object on effbot.org for a rather extensive discussion on the defintions of the various terms, on history, and on the flaws in some of the arguments for the terms pass by reference and pass by value.

    The short story, without naming it, goes like this:

    • Every variable, object attribute, collection item, etc. refers to an object.
    • Assignment, argument passing, etc. create another variable, object attribute, collection item, etc. which refers to the same object but has no knowledge which other variables, object attributes, collection items, etc. refer to that object.
    • Any variable, object attribute, collection item, etc. can be used to modify an object, and any other variable, object attribute, collection item, etc. can be used to observe that modification.
    • No variable, object attribute, collection item, etc. refers to another variable, object attribute, collection items, etc. and thus you can't emulate pass by reference (in the C++ sense) except by treating a mutable object/collection as your "namespace". This is excessively ugly, so don't use it when there's a much easier alternative (such as a return value, or exceptions, or multiple return values via iterable unpacking).

    You may consider this like using pointers, but not pointers to pointers (but sometimes pointers to structures containing pointers) in C. And then passing those pointers by value. But don't read too much into this simile. Python's data model is significantly different from C's.

    0 讨论(0)
  • 2020-12-18 10:10

    The Assignment Statements section of the Python docs might be interesting.

    The = statement in Python acts differently depending on the situation, but in the case you present, it just binds the new object to a new local variable:

    def factory(foo):
        # This makes a new instance of Foo,
        # and binds it to a local variable `foo`,
        foo = Foo()
    
    # This binds `None` to a top-level variable `aTestFoo`
    aTestFoo = None
    
    # Call `factory` with first argument of `None`
    factory(aTestFoo)
    
    print aTestFoo.member
    

    Although it can potentially be more confusing than helpful, the dis module can show you the byte-code representation of a function, which can reveal how Python works internally. Here is the disassembly of `factory:

    >>> dis.dis(factory)
      4           0 LOAD_GLOBAL              0 (Foo)
                  3 CALL_FUNCTION            0
                  6 STORE_FAST               0 (foo)
                  9 LOAD_CONST               0 (None)
                 12 RETURN_VALUE        
    

    What that says is, Python loads the global Foo class by name (0), and calls it (3, instantiation and calling are very similar), then stores the result in a local variable (6, see STORE_FAST). Then it loads the default return value None (9) and returns it (12)

    What is the pythonic way of performing that ? Is it a pattern to avoid ? If it is a current mistake, how is it called ?

    Factory functions are rarely necessary in Python. In the occasional case where they are necessary, you would just return the new instance from your factory (instead of trying to assign it to a passed-in variable):

    class Foo(object):
        def __init__(self):
            self.member = 10
            pass
    
    def factory():
        return Foo()
    
    aTestFoo = factory()
    
    print aTestFoo.member
    
    0 讨论(0)
提交回复
热议问题