=== ECE175 Python Lecture Q&A === Questions from students in a class on C: Q1: Without variables declarations, isn't there a problem with mistyped variable names leading to subtle errors? Q2: Is Python's calling method by value or by reference? Q3: How does Python do pointers? === Q1: Without variables declarations, isn't there a problem with mistyped variable names leading to subtle errors? A1: It can happen, but the subtle errors are rather rare. Usually you will get an immediate and clear message like: Traceback (most recent call last): - - - NameError: name 'z' is not defined where 'z' is the name you intended to type. The subtle error can occur if z is already defined, and you intended to re-define it. If you worry about errors like this, you can scan your program with a utility like pychecker, and it will detect any variables that are defined but never used. The only time I use type checking is when I need to validate user input. In that case, I just add a few 'assert' statements to the function: def checkRawDataFromUser(stringoober, ml): assert isinstance(stringoober, str) assert isinstance(ml, MegaList) ... === Q2: Is Python's calling method by value or by reference? A2: The phrases call-by-value and call-by-reference have caused a lot of confusion in the Python community. Some authorities say Python is call-by- value {1,2}. Others say it is more like call-by-reference {3} and others prefer entirely different phrases {4,8}. The problem is no agreement on how simple definitions of these terms extend to the more complex calling mechanisms found in modern languages like Python or Java {9}. If you are moving from C to Python, the important thing to understand is that when you pass an object to a function in Python, you are actually passing a pointer to that object, and the object may be unexpectedly modified by the function. So in this sense, it is like call-by-reference in C. For those who want to pursue this question, we need to look in detail at the sequence of events in passing an argument to a function, and compare it to what happens in C. C is call-by-value {6}, but because we have direct access to references (pointers) we can make it do a call-by-reference. In C, the type of an "object" (char, int, float, etc.) is associated with the variable name, not the object. variable:(name, type, pointer) --> object:(value, address) A call-by-value copies the caller's object value to the function's data area. The original object x is inaccessible to the function, and cannot be modified in any way. Thus it is "protected", the main reason for doing a call-by-value. A call-by-reference copies the caller's object address &x to the function's data area. The function can then use this address as a pointer to access and modify the caller's object value, but not its type or address. This is an efficient way to pass a large object without making a copy, the main reason for doing a call-by-reference. Note: A call-by-reference in C is done by performing a call-by-value, with the value being an explicitly computed address &x. In both cases the underlying mechanism is call-by-value. In Python, type is associated with the object, not the variable name. {7} variable:(name, pointer) --> object:(type, value, address) Passing an argument in Python creates an additional variable in the function's namespace, a variable pointing to the caller's object. The object itself is not copied, only a pointer to it. This meets the simple definition of call-by-reference {3,5}. The value of the caller's object may be modified by the function (unless the object is immutable). >>> def f(y): y[:] = range(9) >>> x = ['abc', 'xyz'] >>> f(x) >>> x [0, 1, 2, 3, 4, 5, 6, 7, 8] As this example suggests, you can replace the entire contents of the caller's object, but not change its type or address. You cannot assign a new object to y and expect that change to be seen by x. This will only re-bind the name y to the new object. This re-binding behavior has nothing to do with the calling mechanism, but is perhaps the reason why some don't see Python's calling method as a true "call-by-reference". {1} Martelli, Python in a Nutshell, 2nd ed. p.74. {2} Zelle, Python Programming, p.184. {3} Goldwasser & Letscher, Object-Oriented Programming in Python, section 10.3.1, p.351 - a clear explanation of call-by-value and call-by-reference, using the original simple definitions. {4} Lundh, Call by Object, http://effbot.org/zone/call-by-object.htm {5} Wikipedia - Call by Value {6} Kernighan & Ritchie, 2nd ed. p.27. {7} Lundh, Python Objects - http://effbot.org/zone/python-objects.htm - a clear and detailed explanation of Python objects and variables. {8} Python Tutorial, Guido van Rossum, Section 4.6 The actual parameters (arguments) to a function call are introduced in the local symbol table of the called function when it is called; thus, arguments are passed using call by value (where the value is always an object reference, not the value of the object). Actually, "call by object reference" would be a better description, since if a mutable object is passed, the caller will see any changes the callee makes to it (items inserted into a list). {9} Bruce Eckel, "Pass by Value", p.1018 in Thinking in Java, 2nd ed. Nice summary of the views of "two distinct camps". "I will attempt to sidestep the issue. In the end, it isn't that important. What is important is that you understand that passing a reference allows the caller's object to be changed unexpectedly." === Q3: How does Python do pointers? A3: Python's pointers are "under the hood", not something the programmer sees or ever needs to worry about. This part of what makes Python a higher level language than C. You gain a lot in simplicity and readability, while losing the ability to work directly with memory addresses. As a C programmer, you may wonder "What if I really need pointers for more than just efficiency? Sometimes I want a pointer so I can do a call-by- reference, and modify the caller's argument from within a function." The answer is, you can do this in Python even without pointers. See the example above where we modified a list object. Some small objects in Python are immutable, but it is typically large objects where you need to modify the caller's argument (to avoid the overhead of copying). Large objects in Python are typically mutable. Note: You can get the address of an object x with the function id(x), but this is used only to provide a unique identity for the object, not to access it. If id(x) == id(y), you know that variables x and y are not just identical in value, but both point to the exact same object.