Discussion:
[Tutor] Variable reference
Suresh Nagulavancha
2015-07-06 13:55:10 UTC
Permalink
Hello everyone
I want to know about the variables dereference
Code is in python 27
Let my variable be
foo="hello python"
Print foo
del foo
What del command here actually doing , is it dereferencing or deleting the variable along with value it stored?

Thank you

Suresh Nagulavancha
_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Alan Gauld
2015-07-06 20:44:38 UTC
Permalink
Post by Suresh Nagulavancha
Hello everyone
I want to know about the variables dereference
Code is in python 27
Let my variable be
foo="hello python"
Print foo
del foo
What del command here actually doing
Python variables are stored internally in a dictionary.
del removes the name/value pair from the dictionary.

If there are no more references to the object then the
garbage collector will eventually remove it from
memory. But you shouldn't need to worry too much
about that.
--
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.amazon.com/author/alan_gauld
Follow my photo-blog on Flickr at:
http://www.flickr.com/photos/alangauldphotos


_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Danny Yoo
2015-07-07 00:18:16 UTC
Permalink
I'd also add that the 'del' statement has near-zero utility.

'del' is a language blemish. It should not be used by beginners,
because it asks them to try to manually manage the lifetime of their
variable names. That's an unreasonable and ridiculous burden.
Functions have local variables for a reason.
_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Alan Gauld
2015-07-07 00:38:01 UTC
Permalink
Post by Danny Yoo
I'd also add that the 'del' statement has near-zero utility.
'del' is a language blemish. It should not be used by beginners,
because it asks them to try to manually manage the lifetime of their
variable names. That's an unreasonable and ridiculous burden.
Functions have local variables for a reason.
I don't know that I'd go that far. There are valid uses for
it in deleting things from dictionaries and the like.
But I agree its not needed very often and can lead to
users over-managing their data.
--
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.amazon.com/author/alan_gauld
Follow my photo-blog on Flickr at:
http://www.flickr.com/photos/alangauldphotos


_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Peter Otten
2015-07-07 14:08:30 UTC
Permalink
Post by Alan Gauld
Post by Danny Yoo
I'd also add that the 'del' statement has near-zero utility.
'del' is a language blemish. It should not be used by beginners,
because it asks them to try to manually manage the lifetime of their
variable names. That's an unreasonable and ridiculous burden.
Functions have local variables for a reason.
I don't know that I'd go that far. There are valid uses for
it in deleting things from dictionaries and the like.
For dicts and lists a method would work as well. Even now you can write

items.pop(index) # instead of del items[index]
lookup.pop(key) # del lookup[key]

If you find the name pop() random or hard to discover a delete() method
could be added.

globals().pop("name") # instead of del name in the global namespace
delattr(obj, "name") # del obj.name

For the above the replacement is less elegant, but I don't think it matters
for a rarely used feature. So for everything but local and nonlocal names
del is syntactic sugar at best.
Post by Alan Gauld
But I agree its not needed very often and can lead to
users over-managing their data.
_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Steven D'Aprano
2015-07-07 15:45:34 UTC
Permalink
Post by Peter Otten
For dicts and lists a method would work as well. Even now you can write
items.pop(index) # instead of del items[index]
lookup.pop(key) # del lookup[key]
If you find the name pop() random or hard to discover a delete() method
could be added.
globals().pop("name") # instead of del name in the global namespace
delattr(obj, "name") # del obj.name
For the above the replacement is less elegant, but I don't think it matters
for a rarely used feature. So for everything but local and nonlocal names
del is syntactic sugar at best.
Not so. The point of del being a statement is that it should be
considered an operation on the *reference*, not the *value* of the
reference. So:

x = 23
delete(x) # if it existed, it would see the value 23
del x # operates on the reference "x", not 23

We can work around this by quoting the variable name:

delete("x") # this could work

but that is not an elegant design. It's a work-around for the fact that
Python doesn't have dedicated syntax to say "operate on the reference
foo" rather than the value of foo.

In Python, I think there are only two operations on references
themselves: binding, and unbinding. For some purposes, we can consider
unbinding just a special case of binding. (For example, adding a `del
spam` line to a function makes spam a local, just as assigning to it
would.) All binding operations are firstly statements, not function
calls:

x = 23 # not assign("x", 23)
import spam
for eggs in sequence
with expr as cheese
except SomeError as foo


and del is no exception. For some of these, there are functional
versions: setattr, delattr come to mind, but I don't think there are
many others. dict.pop and similiar are not conceptually the same, as
they don't operate on references, they operate on keys, indexes, names
as strings, etc.

I acknowledge that there is some overlap between the two, and one can
replace the other (at least sometimes), but conceptually they operate in
different spheres.
--
Steve
_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Peter Otten
2015-07-07 16:50:25 UTC
Permalink
Post by Steven D'Aprano
Post by Peter Otten
For dicts and lists a method would work as well. Even now you can write
items.pop(index) # instead of del items[index]
lookup.pop(key) # del lookup[key]
If you find the name pop() random or hard to discover a delete() method
could be added.
globals().pop("name") # instead of del name in the global namespace
delattr(obj, "name") # del obj.name
For the above the replacement is less elegant, but I don't think it
matters for a rarely used feature. So for everything but local and
nonlocal names del is syntactic sugar at best.
Not so. The point of del being a statement is that it should be
considered an operation on the *reference*, not the *value* of the
x = 23
delete(x) # if it existed, it would see the value 23
del x # operates on the reference "x", not 23
Read again. I said that

del x

in the global namespace can be emulated with

globals().pop("x")

and that there is no equivalent to

del x

if x is a local/nonlocal name.
Post by Steven D'Aprano
delete("x") # this could work
but that is not an elegant design.
I agree that if you think that explicitly unbinding a name is a useful
feature of the language a statement is the way to go. For me it's a feature
I hardly ever use and that I hardly ever find compelling in other people's
code. I'd happily resort to

x = None

should the need arise to dereference a specific variable.
Post by Steven D'Aprano
It's a work-around for the fact that
Python doesn't have dedicated syntax to say "operate on the reference
foo" rather than the value of foo.
I think Danny's point was that you should not micromanage name bindings at
all. Then Alan added that del is useful for dicts etc. on which I replied
that a method would be sufficient for that.
Post by Steven D'Aprano
In Python, I think there are only two operations on references
themselves: binding, and unbinding. For some purposes, we can consider
unbinding just a special case of binding. (For example, adding a `del
spam` line to a function makes spam a local, just as assigning to it
would.) All binding operations are firstly statements, not function
x = 23 # not assign("x", 23)
import spam
for eggs in sequence
with expr as cheese
except SomeError as foo
and del is no exception. For some of these, there are functional
versions: setattr, delattr come to mind, but I don't think there are
many others. dict.pop and similiar are not conceptually the same, as
they don't operate on references, they operate on keys, indexes, names
as strings, etc.
I acknowledge that there is some overlap between the two, and one can
replace the other (at least sometimes), but conceptually they operate in
different spheres.
I'd put that the other way round: allowing both

del a["x"] # invokes a method on `a`

and

del x # manipulates a namespace

glosses over the "conceptual difference"; if a clean approach were the goal
only the latter should be allowed.

_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Danny Yoo
2015-07-07 21:01:48 UTC
Permalink
Post by Peter Otten
Post by Steven D'Aprano
It's a work-around for the fact that
Python doesn't have dedicated syntax to say "operate on the reference
foo" rather than the value of foo.
I think Danny's point was that you should not micromanage name bindings at
all. Then Alan added that del is useful for dicts etc. on which I replied
that a method would be sufficient for that.
Yes. Apologies for not being clear.

I was thinking of the two recent uses of 'del' showing up on the
mailing list in the past week or so. Both of the uses, from different
authors, were trying to manually manage name bindings. It made the
code harder to understand. In both cases, both uses of 'del' were
ineffective.

When I'm reading code, I want to know statically what my variables
are. By statically, I mean that I should be able to tell, just be
reading the code, what the code means, without running it. "What
variables are accessible?" is one of the most basic questions I ask
myself when I'm reading code.

But if we use 'del' on name bindings, that makes the set of accessible
variables a dynamic property that, in the most general case, requires
us to run the code to figure it out.

When I'm trading a static property for a dynamic property, I want to
get a useful amount of power for that tradeoff, because it's costly.
In my opinion, 'del' on a name binding is just not useful enough to be
worth that conceptual cost. 'del' to remove attributes or dictionary
key/value pairs is a different matter. I want to constrain my
objections to 'del' specifically to its use on name bindings.

That all being said: wow. I'm getting a heck of a lot more
curmudgeon-y these days. :P
_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Steven D'Aprano
2015-07-08 00:54:24 UTC
Permalink
On Tue, Jul 07, 2015 at 06:50:25PM +0200, Peter Otten wrote:
[...]
Post by Peter Otten
Post by Steven D'Aprano
Not so. The point of del being a statement is that it should be
considered an operation on the *reference*, not the *value* of the
x = 23
delete(x) # if it existed, it would see the value 23
del x # operates on the reference "x", not 23
Read again. I said that
del x
in the global namespace can be emulated with
globals().pop("x")
and that there is no equivalent to
del x
if x is a local/nonlocal name.
Yes, I read all that. There *could* (at least in theory) be an
equivalent to `del x` using a function, which I called `delete("x")`,
that applied to locals/nonlocals as well as globals. It would still not
be *close* equivalent to del, because they operate in different spheres,
semantically.

Using pop() is not quite right, because it doesn't just remove the
key:value pair, it *returns the value*. So that's a another difference
between popping a key from the globals, and del:

del x:
- operates on the variable x, not the value of x
- unbinds that variable
- has no return result (is a statement, not an expression

globals().pop("x"):
- operates on a value, the string "x", not a variable
- conceptually, not an unbinding operation at all
- returns the value bound to the variable x

This makes a practical difference at the interactive interpreter:
popping as two side effects, only one of which is intended:

py> a, b = 1, 2
py> del a
py> globals().pop("b")
2


It's unlikely to change, since the current semantics of globals() is
documented as a language feature, but in principle at least a Python
implementation might optimize the language by making globals() more like
locals(), i.e. calling globals() returns a *copy* of the global
namespace, not the namespace itself.

As I said, this is unlikely to change without a period of deprecation,
but still, del is *intended* to unbind variables, pop is not. The fact
that globals().pop also unbinds them is an accident of the way manages
globals.

For all these reasons, if I saw

globals().pop("x")

in code I was reviewing, I would change it to `del x` without
hesitation. Not withstanding the fact that we *can* replace del with pop
as above, we shouldn't.


[...]
Post by Peter Otten
I agree that if you think that explicitly unbinding a name is a useful
feature of the language a statement is the way to go. For me it's a feature
I hardly ever use and that I hardly ever find compelling in other people's
code.
I completely agree that del is rarely useful! But *rare* is not *never*.

There are two compelling cases for using del on names: explicitly
managing the names in a namespace (i.e. to avoid "namespace pollution")
and avoiding long-lived global references to objects which you no
longer need.

I agree that using del on a local variable is probably unnecessary
micro-management. I can't think of any scenario where you would want to
manually del a local variable. If you did, that's possibly a sign that
your function is too big.

When writing a script or application, name management is not a big deal.
But in a library, temporary variables are pollution. They make it harder
for the users of your library to tell what's part of the API and what
isn't, and they make "from module import *" less useful. So if I have a
temporary global variable, I may want to get rid of it once I'm finished
with it. Here's a snippet from a library module I have, designed to be
used with * imports:

tmp = set(C0.keys()) & set(C1.keys())
assert not tmp, 'duplicate control code acronyms: %s' % tmp
# Special check for SCG abbreviated acronym.
assert 'SGC' not in C0
assert 'SGC' not in C1
# Validate that the ^ and ESC codes are correct.
for C in (C0, C1):
for cc in C.values():
assert cc.code == _code(cc.ordinal), 'failed check: %s' % cc
del C, cc, tmp

Those three temporary variables, C, cc and tmp, would otherwise hang
around forever, polluting the namespace and confusing my module's users.
(My module's users, so far, is mostly me, but I'm easily confused.)
Post by Peter Otten
I'd happily resort to
x = None
should the need arise to dereference a specific variable.
Ah, reading that makes me sad, because it looks like you are not getting
the difference between a *variable* (a name in a namespace) and the
*value* of that variable.

`x = None` does not remove the variable from the namespace, it just
binds it to None. So it is no substitute for the del statement.
Post by Peter Otten
Post by Steven D'Aprano
It's a work-around for the fact that
Python doesn't have dedicated syntax to say "operate on the reference
foo" rather than the value of foo.
I think Danny's point was that you should not micromanage name bindings at
all. Then Alan added that del is useful for dicts etc. on which I replied
that a method would be sufficient for that.
Sure, Python *could* have used methods for deleting a key from a dict,
or a slice from a list:

mydict.delete(key) # like del mydict[key]

mylist.delete(1, 20, 3) # like del mylist[1:20:3]

But note that introduces a source of API confusion: people may use
mylist.remove(1) when they mean delete, or the other way around. Either
way, that error is harder to make with the del statement: there is no
reasonable way for a person to be confused about whether

del mylist[3]

removes the 3rd item, or an item with the value 3. With:

mylist = [3, 6, 9, 12]
mylist.delete(3)

it isn't clear whether you end up with [3, 6, 9] or [6, 9, 12].

And when it comes to attributes, we shouldn't use getattr, setattr or
delattr with constant arguments:

# Yes
y = x.spam
x.spam = 23
del x.spam

# No
y = getattr(x, "spam")
setattr(x, "spam", 23)
delattr(x, "spam")

I wouldn't hestitate to replace any of the second set with the version
from the first set.
--
Steve
_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Peter Otten
2015-07-08 12:54:55 UTC
Permalink
Post by Steven D'Aprano
When writing a script or application, name management is not a big deal.
But in a library, temporary variables are pollution. They make it harder
for the users of your library to tell what's part of the API and what
isn't, and they make "from module import *" less useful. So if I have a
temporary global variable, I may want to get rid of it once I'm finished
with it. Here's a snippet from a library module I have, designed to be
tmp = set(C0.keys()) & set(C1.keys())
assert not tmp, 'duplicate control code acronyms: %s' % tmp
# Special check for SCG abbreviated acronym.
assert 'SGC' not in C0
assert 'SGC' not in C1
# Validate that the ^ and ESC codes are correct.
assert cc.code == _code(cc.ordinal), 'failed check: %s' % cc
del C, cc, tmp
Those three temporary variables, C, cc and tmp, would otherwise hang
around forever, polluting the namespace and confusing my module's users.
(My module's users, so far, is mostly me, but I'm easily confused.)
You are not alone with that design. The first thing that came up in the
stdlib was the webbrowser module:

$ cd /usr/lib/python3.4
$ find . -name \*.py -print0 | xargs -0 egrep '\bdel\s+(_|[[:alnum:]])+$' |
head -n1
./webbrowser.py: del cmdline
$

Here's the relevant part of the code where the author manages to del two out
of three helper variables:

"""
# OK, now that we know what the default preference orders for each
# platform are, allow user to override them with the BROWSER variable.
if "BROWSER" in os.environ:
_userchoices = os.environ["BROWSER"].split(os.pathsep)
_userchoices.reverse()

# Treat choices in same way as if passed into get() but do register
# and prepend to _tryorder
for cmdline in _userchoices:
if cmdline != '':
cmd = _synthesize(cmdline, -1)
if cmd[1] is None:
register(cmdline, None, GenericBrowser(cmdline), -1)
cmdline = None # to make del work if _userchoices was empty
del cmdline
del _userchoices
"""

I really don't like that approach; I prefer writing a helper function,
properly marked as an implementation detail, e. g.

def _setup_userchoices(userchoices=None):
"""
Post by Steven D'Aprano
_setup_userchoices(["one"])
_tryorder[0]
'one'
Post by Steven D'Aprano
_setup_userchoices(["two", "three"])
_tryorder[:3]
['two', 'three', 'one']
"""
if userchoices is None:
userchoices = os.environ.get("BROWSER", "").split(os.pathsep)
for cmdline in reversed(userchoices):
if cmdline != "":
cmd = _synthesize(cmdline, -1)
if cmd[1] is None:
register(cmdline, None, GenericBrowser(cmdline), -1)

_setup_userchoices()

That is easy to test (if this were for real you'd use unit test, not
doctest), and you don't have to decide whether the leftover `cmd` is kept
intentionally or by accident, or be careful to avoid that you tread on names
that *are* part of the API.

That are the advantages for the author/reader of the module. As a user the
extra _setup_userchoices() function doesn't bother me at all.


_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor

Steven D'Aprano
2015-07-07 02:07:44 UTC
Permalink
Post by Danny Yoo
I'd also add that the 'del' statement has near-zero utility.
'del' is a language blemish. It should not be used by beginners,
because it asks them to try to manually manage the lifetime of their
variable names. That's an unreasonable and ridiculous burden.
Functions have local variables for a reason.
Not all variables are local variables, and del exists to manage more
than just name bindings. Deleting attributes and items from sequences
are good uses for it, as is deleting global names which are not needed.

You are right that del should not, generally, be used by beginners, and
especially not for manually managing names. Fortunately, beginners are
not usually inclined to write code like this:

def spam(s):
a = s.upper()
b = s + "s"
process(a, b)
return do_something_else(a, b)
del a, b, s

as that would be both pointless and silly. Not only is the del line
never reached by Python, but the local variables are automatically
deleted when the function returns, so it is a waste of programmer time
and effort to manually delete them.
--
Steve
_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Steven D'Aprano
2015-07-07 01:58:46 UTC
Permalink
Post by Suresh Nagulavancha
Hello everyone
I want to know about the variables dereference
First you need to know how variables reference.

When you assign a value to a variable, we say that we "bind the value to
the variable's name":

spam = 42

tells Python to bind 42 to the name "spam", which associates the value
42 with the name "spam". Every program has a global scope and local
scopes for each function. We call those "namespaces", and that is where
Python tracks the association between names and values.

In practice, Python often uses a dict for such namespaces, but not
always. 99.9% of the time, you don't need to care about that, just let
Python manage the variable names.
Post by Suresh Nagulavancha
Code is in python 27
There is no Python 27. I think you mean "Python 2.7" (two point seven).
Post by Suresh Nagulavancha
Let my variable be
foo="hello python"
Print foo
That is a syntax error. As a programmer, you must be precise and
accurate about what you say. "foo" and "Foo" and "FOO" are not the same
thing, neither is "print" and "Print" and "PRINT".
Post by Suresh Nagulavancha
del foo
What del command here actually doing , is it dereferencing or deleting the variable along with value it stored?
del unbinds the value from the name and removes the name from the
current namespace. To put it another way, it deletes *the variable* but
not the value.

Here is another example:

spam = "Hello world!"
eggs = spam
# these 2 lines can be written as 1: spam = eggs = "Hello world!"


At this point, there are two references to the string "Hello world!":
the two names (variables), "spam" and "eggs". We can print them, pass
them to functions, etc.

del spam

This removes the binding from variable "spam" to the string. The string
itself is not deleted, only the name binding ("the variable spam"). At
this point, we can still write:

print eggs

which is okay, because the variable eggs still exists and is still
bound to the string. But this line:

print spam

raises an exception, since the variable spam no longer exists. The
string itself, "Hello world!", is not deleted until the last reference
to it is gone.


Hope this is clear.
--
Steve
_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Continue reading on narkive:
Loading...