On Tue, 17 Oct 2017 02:40 pm, Ben Finney wrote: > Steve D'Aprano <steve+pyt...@pearwood.info> writes:
>> `del` cannot be a method or a function, because the argument to `del` >> is the name of the variable, not the contents of the variable. > > Since a Python “variable” does not have contents, this is IMO another > instance where using the term “variable” is not helpful. > > >>> x = 123 > >>> y = [0, x, 2, x, 4] > > Neither of the names ‘x’ nor ‘y’ have content; What I meant by "content" was "value". In hindsight, I don't even know why I wrote the word "content". It was not a good choice of words. Mea culpa. Of course the variables "x" and "y" have values: the value of x is 123, and the value of y is the specific list bound to the name "y" in the appropriate namespace. If you don't agree with that, then our terminology is so far apart that I don't believe we can ever agree, or understand each other. The point is, if del were a function, then calling del(x) would pass the *value* of x into the function, not the name 'x' -- unless the interpreter made del a special case, not a regular function. But if the interpreter did that, why bother with function notation and the extraneous parentheses? Why not just make it a statement and avoid surprising the programmer by giving the del function super-powers that no other function has? Which, of course, is exactly what Guido did. > they are references to objects. You are conflating the implementation details of how the Python VM implements name binding (a mapping between names and references to objects) and the high-level semantics of Python code. In CPython, at least, such references are pointers. They're something analogous in Jython and IronPython. It is sometimes useful to peer under the hood and talk about the implementation of the VM, but I don't think this is one of those times. There are too many inconsistencies in how we need to use the word "reference" to make this strategy viable -- see below. [...] > So when we give an argument to ‘del’, that argument is not always a > “variable”; it is always a reference. The argument to del is a comma-separated list of l-values, that is, the kind of expression which is legal on the left-hand side of an assignment statement. That includes: - bare names like `x` - qualified names like `module.x` or `instance.attribute` - items in mappings like `mydict[key]` - items in sequences like `mylist[0]` - lists and tuples of such l-values, e.g. this is legal: py> a, b, c, d = 1, 2, 3, 4 py> del a, [b, c], d as well as more complex examples. But you can't pass an arbitrary "reference" to del and expect something sensible: del math.sin(1) would, if it were legal, evaluate the expression math.sin(1) and return the float 0.8414... (or, if you prefer to talk about the *implementation*, return a reference to the float object) and then try to delete it in some sense. But that's not legal, and you get a syntax error at compile time. If del accepted references in the usual sense, this would be legal but pointless: del would receive the reference to the float object, and delete the reference, leaving the float to possibly be garbage collected. But that's not what del does. > We can delete one item from a list, because that item is accessed via a > reference; we give the same reference as argument to ‘del’:: > > >>> del y[1] In context, I was specifically referring to unqualified names because deleting list items *can* be done with a method call. In fact, the above line ends up calling y.__delitem__[1]. In an alternative universe, Guido might have forgone the `del` statement and required us to call: list.pop(1) or similar for dicts and other collections. The OP was already thinking along those lines when he or she asked the question, so I didn't think I needed to cover that case. But the reason del is a statement is that it *also* covers cases where we cannot use a regular method or function, and the simplest such case is unbinding a name. Now, to go back to your comment that "we give the same reference as argument to ‘del’" that's incorrect, or at least, in order for it to be correct we must use *two* different meanings to the word "reference", depending on the context. If I write: y = [None, "Aardvark", None, None] print(y[1]) # (A) del y[1] # (B) then the same descriptive text: "the reference y[1]" needs to be understood in two different ways: - in line (A) y[1] is a reference to the string object "Aardvark"; - in line (B) y[1] is *not* a reference to the string object, but stands for the 1st position in the sequence referred to by `y`. So it is misleading, or at least surprising, to use the term "reference" in both situations. It isn't clear whether you mean reference to the value or reference or not. In logic and philosophy, we often need to think about the distinction between *using* a word and *mentioning* the word: https://en.wikipedia.org/wiki/Use%E2%80%93mention_distinction Failing to distinguish the two leads to fallacies: https://www.logicallyfallacious.com/tools/lp/Bo/LogicalFallacies/180/Use-Mention-Error some of which are pretty obvious ("Python is not a programming language, it is a six-letter word"), and others not so obvious. Python has an analogous distinction: (1) Python's evaluation of an expression is normally analogous to *using* a word. For example: print(spam.eggs) the interpreter evaluates the expression `spam.eggs`, returning an object of some kind (often called an "r-value", or just a value), then passes the object to print, which prints it. (2) But binding operations (including unbinding) are analogous to *mentioning* an expression (with the condition that only certain kinds of expressions are legal as assignment targets). For example: del spam.eggs does not evaluate the expression `spam.eggs` and pass the resulting object to del. That's why del cannot be a function: it needs to operate on its arguments before the interpreter evaluates them to objects. Of course, there is a way that we could make del a regular function, even for unqualified names[2]: we could quote the arguments ourselves. So to delete the bare name "x" from the global scope, we could write: delete("x", globals()) To delete an attribute or qualified name, we could write: delete("attribute", instance) # or use delattr(instance, "attribute") delete("x", module) To delete an item from a list: delete(1, mylist) # or use mylist.pop(1) and so forth. All pretty clumsy and ugly, but it could work. So the ultimate reason why del is a statement rather than a function is that Guido wanted to be able to unbind r-values without having to quote them first. [1] Except in the loosest sense that any use of words to refer to a concept is a reference, e.g. the words "Queen Elizabeth" is a reference to the current monarch of the UK, Elizabeth Windsor. [2] Almost. It would be difficult or impossible to delete a local (but why would you want to?), a nonlocal, or a class-level name binding from inside the class. E.g.: class K: x = 1 del x -- Steve “Cheer up,” they said, “things could be worse.” So I cheered up, and sure enough, things got worse. -- https://mail.python.org/mailman/listinfo/python-list