Python Forum
Python Has Major Problems as Language
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Python Has Major Problems as Language
#1
I will apologize ahead of time here. Python is great, and my preferred language, but it needs to have basic flaws fixed:

1.)Some variables are passed by reference and some are passed by value, and we have no control which causes a huge number of other problems. If you Python compiler people would disallow raw access to the raw address of a variable, you could stop calling it pass-by-object, so you can stop lecturing programmers on this, and stop polishing your excrement. I don't care if it's mutable or not, that's your problem, Python compiler people. I don't want to bury everything in a list to make it pass by reference, that's clumsy.

2.)Python does not allow you to create a unique variable. It hijacks them at will. If you need to make copies of something, say a list, it will randomly hijack your copy in a non-deterministic way, based on how you use it, and code that previously worked will break. For some things, like a class, you just can't copy it without a bunch of clumsy code.
Reply
#2
Please read who is python-forum.io first to understand that we are not the Python language core developers.

That said, could you post code examples of what you mean by these major problems. Python passes everything by reference, but it cannot pass variable names by reference, so a function cannot set a variable of the calling function's body. This is only annoying if you have strong habits acquired in languages that allow this, but these habits can be changed. I for example almost never bury anything into a list to allow call by reference. Functions can set their argument's attributes and this generally suffices.
buran likes this post
Reply
#3
With all these flaws it is amazing that any Python program ever works. Sarcasm aside I would love some examples.
buran likes this post
Reply
#4
Sorry for being so late to this thread.

I believe that Dave is confused about Python's behaviour because he is stuck thinking about it in terms of passing arguments to functions by value versus passing them by reference. Python does neither of these things.

(On the other hand, Dave seems to know full well why Python behaves the way it does, he seems to understand that pass by value and pass by reference are not the only argument passing strategies available, he just wants Python to behave like some other language. Maybe Visual Basic, or C++.)

Let's start with what I imagine Dave's complaint is. Here is a simple example.

def add_and_print(obj, thing_to_add):
    obj += thing_to_add
    print(obj)

# Set up some variables with values in them.
x = 100
y = ['a', 'b']

add_and_print(x, 1)  # Pass the variable x as an argument.
print("x is", x)  # Has x changed? No.

add_and_print(y, ['c'])  # Pass the variable y as an argument.
print("y is", y)  # Has y changed? Yes.
If you run that code, you will see that after calling the function add_and_print the variable x keeps its old value, but the variable y has taken on the changed value. This is, in fairness to Dave, a little surprising, until you understand what is going on.

One explanation of the observed differences in behaviour would be:

"When you pass the integer x to a function, the compiler passes the variable "by value", making a copy of the integer and passing it into the function. So changing that argument inside the function only changes the copy, not the original.

But when you pass the list y to the function, the compiler passes the variable "by reference" (i.e. a pointer to the original), not a copy, so changes to the argument inside the function affect the original variable y."

If you imagine that there are only two possible ways to pass a variable to a function, by value or by reference, then this explanation seems to be the only possible explanation. Passing an integer makes a copy of it, passing a list passes a reference to the original list.

This is wrong. That is not what happens in Python. Exactly the same thing occurs in other popular languages like Java and Javascript.

Some of the confusion comes from an assembly language view of computer languages. At the very lowest software levels, namely assembly language or machine code, there actually are only two ways you can pass a value into a function: you can make a copy of the value, or you can pass an indirect reference (a pointer) to the original. If you think in terms of, say, C language, that low-level view is all there is.

But in terms of higher level languages like Python and many others, there are many more options.

Even in Python, if you look under the hood at the details of how the interpreter works at the assembly language level of the code, the interpreter passes references (pointers to memory addresses) by value (i.e. making a copy of the pointer). This low-level view of the world is why you will sometimes see people insist that Java is call by value, but to do so, they have to make the astonishing claim that when you pass a value like 100 to a function, the actual value being passed is not 100 but some hidden, abstract "value" like 0x7f056d5a9dc0.1

To quote the Effbot:

"Joe, I think our son might be lost in the woods"

"Don't worry, I have his social security number"



While the deepest levels of the interpreter might operate by copying memory addresses, that's not very helpful to know. What we care about is not the invisible, inaccessible pointers to memory addresses of bits in memory, but the high-level objects like the int 100 and a list of characters.

So at the level of Python objects, what actually happened?

Regardless of whether you are passing 100 or a list, regardless of whether the argument comes from a variable, a constant or an expression, the Python interpreter passes the object itself into the function. So add_and_print(x, 1) passes the 100 object itself to the function, not a copy. Same when you pass y, the list object ['a', 'b'] gets passed.

Python always uses the same argument evaluation strategy, regardless of the type of object.

The difference between the two cases is not how the object is passed to the function, but what happens inside the function. Because ints are immutable, the line obj += thing_to_add has to create a new int object, assigning it to the local variable inside the function. This leaves the global variable x outside the function unchanged.

But when you pass a list, the += assignment operator behaves differently. Because lists are mutable, it doesn't create a new list object, it modifies the existing list object. And so the change becomes visible outside the function via the global variable y.

Is this good? Bad? Both?

It's neither good nor bad. Its just different from the old-school 1970s style of pass by value and pass by reference as used in Pascal and BASIC. All different evaluation strategies and parameter passing strategies have their strengths and weaknesses. The strategy used by Python (and Java, Javascript, Perl, Ruby and many others) is convenient for the writers of the interpreter or compiler, it matches naturally with the way people think about objects in the real world, and if you really need to match the behaviour of other languages:
  • to simulate pass by value, just make a copy of your object before passing it;
  • to simulate pass by reference, put your variable inside a list, and then use that list item as an indirect reference to the variable.


1 Note for Java experts: I'm referring to "boxed" Java values, or objects, not unboxed low-level machine values, which are passed by value (i.e. by making a copy of the actual value 100).
Reply
#5
Further to my previous reply, we can test the hypothesis of whether Python uses pass-by-value or pass-by-reference. (It doesn't, but don't take my word for it.)

The defining characteristic of pass-by-value is that arguments are copied when you pass them into a function. So we can check whether the argument is copied or not by checking its id() inside and outside the function.

(There is a small subtlety here: object IDs are only unique while the object exists. If you record an ID, then delete the object, that frees up the ID to be used again by another object. In Jython and IronPython, that is not likely to happen, but in CPython it happens regularly.)

Here is a function which can test whether or not the passed in object is a copy or not. To use it, you pass in the object and its ID (no cheating!):

def check_for_pass_by_value(obj, obj_id):
    if id(obj) != obj_id:
        print("looks like a copy (unless you cheated)")
    else:
        print("not a copy")
Here is an example of how to use it fairly, without cheating:

value = [1234.5, 'abc']  # Anything you like here.
check_for_pass_by_value(value, id(value))
And here is how to cheat, so you know how not to do it by accident:

# pass two different objects that happen to have the same value
check_for_pass_by_value([1234.5, 'abc'], id([1234.5, 'abc']))
If you can find any object at all, anything, where the non-cheat version reports that a copy was made, then you have proven that Python at least sometimes uses pass-by-value.



The defining test for pass-by-reference is whether or not you can write a swap() procedure that swaps two variables passed in.

def swap(first, second):
    # write some code

a = 1
b = 2
swap(a, b)  # call it as a procedure, ignoring the return value (if any)
assert a == 2 and b == 1
If you can work out how to write that swap() function so that when called with any pair of variables, it swaps them, then you have demonstrated that Python uses call by reference.

Again, it is possible to cheat: e.g. you could hard-code swap() to always swap the global variables a and b. But then it wouldn't work on variables x and y. To be pass by reference, it has to work on any named variable, not just a single pair of hard-coded names.

Another cheat is to just assign the values: b, a = swap(a, b). That's still not pass by reference.

For experts: if you know how to use ctypes, you might find some way to get this swap() procedure to work. Congratulations, and I would love to see it!

But ctypes allows you to dig deep into the mechanics of the interpreter, and do things that aren't part of the Python language. So again, that doesn't count as far as answering the question, "does Python use pass-by-reference?". Yes, it is very clever of you if you can use ctypes to swap two passed in variables, but that's happening deep in the C engine of the interpreter, not in Python itself.

(And besides, other Python interpreters may not have ctypes.)
Reply
#6
And one final response, this time much shorter than the other two.

Dave's complaint is that it is too hard to copy values. That's not true. For lists, it is trivial:
  • Use an empty slice: mylist[:]
  • Use the copy method: mylist.copy()
  • Pass it to the list constructor: list(mylist)
  • Use the copy module: import copy; copy.copy(mylist) # shallow copy
  • Or make a deep copy: import copy; copy.deepcopy(mylist)

All well-designed classes should work with the copy module. If it doesn't, it is probably a bug.
Reply
#7
@stevendaprano I'm afraid these explanations about «call by object» are just the kind of speech that @Dave dislikes in Python «tutorials». Here is my view about this
  • When Python passes a value, it never copies that value. In this sense, Python never «passes by value».
  • If it doesn't pass by value, then it passes by reference, which means that it copies a pointer to the value for every argument passed (at least in CPython).
  • What Python cannot do is pass a reference to an lvalue (a value location), that is to say a reference to a place where a value can be stored. This is a same as passing a pointer to a pointer, or handle, and Python cannot do that.
Passing a pointer or a pointer to pointer are two different things, and that's why the term «call by reference» is misleading. I understood that @Dave regrets that Python cannot pass pointers to pointers, but there is nothing I can do about it.
stevendaprano likes this post
Reply
#8
I think we are mostly in agreement, but to elaborate:

(May-14-2022, 10:10 AM)Gribouillis Wrote: @stevendaprano I'm afraid these explanations about «call by object» are just the kind of speech that @Dave dislikes in Python «tutorials». Here is my view about this
  • When Python passes a value, it never copies that value. In this sense, Python never «passes by value».

This is correct.

Quote:
  • If it doesn't pass by value, then it passes by reference, which means that it copies a pointer to the value for every argument passed (at least in CPython).

  • And this is where people get it wrong. And it is wrong in at least three ways.

    1. Assuming that there are only two options: pass by value or by reference. This is a false dichotomy.
    2. Mistaking CPython implementation details (pointers) for language features.
    3. Failing to take into account the language-level semantics of pass by reference.

    (1) is arguably true if we are discussing the lowest levels of assembly language and machine language (but not at the hardware level, where there is not even "passing values" and everything is just flipping bits), but we're discussing Python, which is a high-level language. How the language is implemented in terms of copying memory locations and/or bit flipping is not relevant to understanding the semantics of Python code.

    At the high level, there are many other calling conventions, including call by name (used in Algol), call by copy-restore, call by need (lazy evaluation) etc, all of which are ultimately implemented by flipping bits. Or if you prefer a slightly higher implementation, by copying *something* from one memory location to another.

    (2) Jython and IronPython are two fully compliant and correct Python implementations which are written in Java and the CLR respectively, neither of which offer access to low-level machine pointers. This is how both Jython and IronPython can offer compacting garbage collection, where objects can move in memory. So if we want to make statements about the semantics of Python, we have to talk about things which are common to all Python implementations -- and that does not include "pointers" (at least not in the sense of low-level machine pointers, a typed memory address).

    And finally, (3) if our description of the semantics of Python doesn't make it clear that we cannot do call by reference things like writing a swap() procedure, then we cannot justify describing Python as "call by reference". We need a different name for it.

    Likewise, if you cannot write Jensen's device in your language, then you either aren't using call by name, or the implementation of it is broken.

    Quote:
  • What Python cannot do is pass a reference to an lvalue (a value location), that is to say a reference to a place where a value can be stored.

  • Correct again, and this is a reason why the "pass by object sharing" model, as used by Python and many other languages, differs from pass by reference. (There may be other differences as well.)

    To justify describing Python's call by sharing semantics as "call by value" (as the Java people do), we have to make the argument that "the value" of a Python expression is not what any actual human programmer thinks of as its value (say, a string like "Hello World" or a number like 1.5), but is some completely invisible and artificial artefact of the implementation like a pointer.

    Ultimately, semantics are more important than implementation. We could simulate a Python interpreter in our head, or using pencil and paper, or in clockwork, or a Turing Machine, without using anything even remotely like pointers to memory locations. And to be Python, we have to emulate the same behaviour, the same semantics, as pass by sharing, *not* pass by value or pass by reference.
    Reply
    #9
    Ok, I understand the «call by object sharing». You mean that Python passes objects but without copying them. I find it simpler to say that python passes references to values, although from the language's perspective, these «references» are ethereal things that we don't see. (still Python has objects IDs)

    If you think about it, the only thing that exist from the language's perspective are names. Variable names in a scope, attribute names in modules or instances, these are the real «handles» to values in Python. This plus the indexed positions in lists and tuples and a few other built-in types. From the programmer's perspective, memory access in python is largely symbolic. One can set an attribute, or an indexed position in a list and that's all the access to memory.
    Reply
    #10
    Just an FYI:

    It is technically possible by using ctypes library. (code is actually embedded C)

    This can be used, for example, to access memory mapped registers, I/O Ports, etc.
    for example in arm cortex m4 which has memory mapped registers.

    see: https://docs.python.org/3/library/ctypes.html
    small example: https://stackoverflow.com/a/8250902
    Reply


    Possibly Related Threads…
    Thread Author Replies Views Last Post
      Can Python be used as a macro language for excel, Word, etc.? CynthiaMoore 8 3,107 Sep-17-2022, 08:12 AM
    Last Post: stevendaprano
      TIOBE Index for October 2021 - Python programming language number 1! Yoriz 1 1,546 Oct-07-2021, 11:35 PM
    Last Post: Larz60+
      a new language based on python Skaperen 0 1,654 Nov-04-2020, 01:54 AM
    Last Post: Skaperen
      Is python an interpreted language or a compiled language? GouravDas 1 2,031 Jun-23-2020, 10:38 AM
    Last Post: Gribouillis
      what programming language to learn other than python in real mass-production product? Kai 1 2,122 Apr-12-2020, 04:40 AM
    Last Post: Larz60+
      Chinese coding language ‘Mulan’ found to be Python fork buran 0 1,727 Jan-22-2020, 04:30 AM
    Last Post: buran
      What is the best book for in-depth Python for advanced other-language programmers? user118967 1 2,672 Jun-04-2019, 07:56 PM
    Last Post: snippsat
      Coconut - A Functional Language Which Compiles to Python nilamo 2 2,723 Jan-04-2019, 12:01 AM
    Last Post: micseydel
      Some Guy Today Told Me Python Isn't A Real Programming Language digitalmatic7 11 10,206 Dec-01-2017, 12:58 PM
    Last Post: I_love_py
      the fastest-growing major programming language dvs1 0 2,199 Sep-07-2017, 02:50 AM
    Last Post: dvs1

    Forum Jump:

    User Panel Messages

    Announcements
    Announcement #1 8/1/2020
    Announcement #2 8/2/2020
    Announcement #3 8/6/2020