0

I am trying to define a function my_del with the following properties:

  • it has only one parameter,
  • if the parameter passed is a variable with global assignment, it deletes it using del,
  • otherwise it prints "Variable not assigned",
  • no NameError exception should be issued.

My first trial:

def my_del(var):
    try: 
       del var
    except NameError:
       print("Variable not assigned")

This does not work.

(i) For an assigned variable:

x = 1
my_del(x)
x
# outputs 1

the reference did not get deleted. Although not sure, I suppose this is because var did not get bound to the global x.

(ii) For an unassigned variable, an exception is issued:

my_del(y)
# NameError exception

The function my_del did not get the chance to be called, as the exception happens before.


My second trial:

def my_del(var):
   try: 
       var = var()
       del var
   except NameError:
       print("Variable not assigned")

(i) For an unassigned variable:

my_del(lambda: y)
# prints the string

This works fine.

(ii) For an assigned variable:

x = 1
my_del(lambda: x)
x
# outputs 1

we still have x referenced. Here var = var() creates a local variable, which does not refer to the input parameter.


What would be a proper way to create this my_del function?

  • 4
    This function doesn't make any sense. Inside the function, the parameter is a parameter; it's locally bound. You cannot have a `NameError` on a function's parameter, until after you've deleted it. As you're seeing, the `NameError` occurs *outside* the function, when you try to resolve the missing identifier to pass its value to the function. Could you give a bit of context around why you think this is a thing you want at all? – jonrsharpe Oct 19 '17 at 11:15
  • @jonrsharpe *Re: "the parameter is a parameter; it's locally bound".* In, e.g., `def foo(var) var.append(5)`, when defining a list `l` and then calling `foo(l)`, can we say that `var` is globally bound? *Re: "context".* I would not have any specific use of this function. I wanted to see if this idea could be implemented (for fun and to learn the language). –  Oct 19 '17 at 11:29
  • No we can't say that, you're just passing in a reference to a mutable object. And without a context this isn't a practical and answerable question of use to others. – jonrsharpe Oct 19 '17 at 11:34
  • @jonrsharpe Thanks for the additional information about the mutable object. –  Oct 19 '17 at 11:39

2 Answers2

1

You have not completely understood what del does. del simply decrements a reference counter to an object. An object is not truly deleted until the number of references pointing to it is 0 (at which point it is garbage collected). At the point of the function call, there are (at least) two references - the function argument, and the original variable passed to it. Furthermore, deleting one reference (var) does not imply deletion of another (x).

To see the number of references currently pointing to an object, you may use:

>>> import sys
>>> sys.getrefcount(x)

Furthermore, if the only purpose of my_del is to (attempt to) delete an object, call del directly...

cs95
  • 379,657
  • 97
  • 704
  • 746
  • 2
    So a bit of a tl;dr - the code is decrementing the reference count by one which wouldn't have been incremented if the function hadn't been called with the reference :) (So a fairly good do-nothing function) – Jon Clements Oct 19 '17 at 11:19
  • @COLDSPEED Thanks for `sys.getrefcount`. The idea of `my_del` is basically a `del` that does not issue a NameError exception if the variable is unassigned. It is not really about how useful it is, it is more about how to implement it. –  Oct 19 '17 at 11:32
  • @unfolx Assuming you are a little cunning with your code design, such things may not be needed... anyway, I hope this gets you a step in the right direction. – cs95 Oct 19 '17 at 11:40
  • @JonClements and which would have been decremented anyway at the end of the function! – jonrsharpe Oct 19 '17 at 11:40
0

You can't do exactly what you're trying to do, but you can get close, by directly manipulating the globals() dict. However, modifying globals() is generally not a good idea. And as as others have said, such a function has no practical use. But anyway...

We can't directly pass the object we want to delete, but we can pass its name.

def my_del(name):
    g = globals()
    if name in g:
        del g[name]
    else:
        print(name, 'unknown')

a = 7
print(a)
my_del('a')
try:
    print(a)
except NameError as e:
    print(e)

output

7
name 'a' is not defined
PM 2Ring
  • 54,345
  • 6
  • 82
  • 182
  • Thanks for answering the question. Very helpful. Happy to learn about `globals()`. –  Oct 19 '17 at 12:31
  • @unfolx No worries. But please don't directly modify `globals()` like that in normal code. It can be handy to inspect the contents of `globals()` during development, but you really shouldn't change it. – PM 2Ring Oct 19 '17 at 12:40
  • @unfolx There's also a `locals()` dict, which is equal to `globals()` in the global context of a module (i.e., outside a function or class definition), but inside a function it holds the local vars of the function. However, modifications to `locals()` may not actually change those local variables. See [Why is it bad idea to modify locals in python?](https://stackoverflow.com/questions/4997184/why-is-it-bad-idea-to-modify-locals-in-python) – PM 2Ring Oct 19 '17 at 12:41
  • OK, warning understood. Thanks for `locals` and the link. –  Oct 19 '17 at 13:46