8

I know that it is a bad idea to name a variable that is the same name as a Python built-in function. But say if a person doesn't know all the "taboo" variable names to avoid (e.g. list, set, etc.), is there a way to make Python at least to stop you (e.g. via error messages) from corrupting built-in functions?

For example, command line 4 below allows me to overwrite / corrupt the built-in function set() without stopping me / producing errors. (This error was left un-noticed until it gets to command line 6 below when set() is called.). Ideally I would like Python to stop me at command line 4 (instead of waiting till command line 6).

Note: following executions are performed in Python 2.7 (iPython) console. (Anaconda Spyder IDE).

In [1]: myset = set([1,2])

In [2]: print(myset)
set([1, 2])

In [3]: myset
Out[3]: {1, 2}

In [4]: set = set([3,4])

In [5]: print(set)
set([3, 4])

In [6]: set
Out[6]: {3, 4}

In [7]: myset2 = set([5,6])
Traceback (most recent call last):

  File "<ipython-input-7-6f49577a7a45>", line 1, in <module>
    myset2 = set([5,6])

TypeError: 'set' object is not callable

Background: I was following the tutorial at this HackerRank Python Set Challenge. The tutorial involves creating a variable valled set (which has the same name as the Python built-in function). I tried out the tutorial line-by-line exactly and got the "set object is not callable" error. The above test is driven by this exercise. (Update: I contacted HackerRank Support and they have confirmed they might have made a mistake creating a variable with built-in name.)

Atlas7
  • 2,726
  • 4
  • 27
  • 36
  • 4
    You can't. You could use linters (e.g. `pylint`) which will check for this kind of thing, but Python allows you to reuse anything except a [keyword](https://docs.python.org/2/reference/lexical_analysis.html#keywords) or `None`. – jonrsharpe Jul 29 '15 at 12:31
  • 1
    FWIW, a decent IDE will point such things out to you. E.g. PyCharm. – deceze Jul 29 '15 at 12:31
  • 2
    You can't prevent it, Python is built on the notion of "we're all consenting adults" so you are free to do what you want. It is possible [to delete the override](https://stackoverflow.com/questions/20885760/how-to-get-back-an-overridden-python-built-in-function?rq=1) but you'd have to know that you shadowed that function in the first place. – Cory Kramer Jul 29 '15 at 12:37
  • Sure, fork Python and write an implementation that won't allow you to use built ins as variable names. – kylieCatt Jul 29 '15 at 12:42
  • There aren't that many [built-in functions](https://docs.python.org/2/library/functions.html) to worry about. – chepner Jul 29 '15 at 13:35

2 Answers2

4

As others have said, in Python the philosophy is to allow users to "misuse" things rather than trying to imagine and prevent misuses, so nothing like this is built-in. But, by being so open to being messed around with, Python allows you to implement something like what you're talking about, in a limited way*. You can replace certain variable namespace dictionaries with objects that will prevent your favorite variables from being overwritten. (Of course, if this breaks any of your code in unexpected ways, you get both pieces.)

For this, you need to use use something like eval(), exec, execfile(), or code.interact(), or override __import__(). These allow you to provide objects, that should act like dictionaries, which will be used for storing variables. We can create a "safer" replacement dictionary by subclassing dict:

class SafeGlobals(dict):
    def __setitem__(self, name, value):
        if hasattr(__builtins__, name) or name == '__builtins__':
            raise SyntaxError('nope')
        return super(SafeGlobals, self).__setitem__(name, value)

my_globals = SafeGlobals(__builtins__=__builtins)

With my_globals set as the current namespace, setting a variable like this:

x = 3

Will translate to the following:

my_globals['x'] = 3

The following code will execute a Python file, using our safer dictionary for the top-level namespace:

execfile('safetyfirst.py', SafeGlobals(__builtins__=__builtins__))

An example with code.interact():

>>> code.interact(local=SafeGlobals(__builtins__=__builtins__))
Python 2.7.9 (default, Mar  1 2015, 12:57:24) 
[GCC 4.9.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> x = 2
>>> x
2
>>> dict(y=5)
{'y': 5}
>>> dict = "hi"
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "<stdin>", line 4, in __setitem__
SyntaxError: nope

*Unfortunately, this approach is very limited. It will only prevent overriding built-ins in the top-level namespace. You're free to override built-ins in other namespaces:

>>> def f():
...     set = 1
...     return set
... 
>>> f()
1
Dan Getz
  • 8,774
  • 6
  • 30
  • 64
  • 2
    Accessing locals happens to be the same as accessing the elements of `locals()`, but the specification does not guarantee this behaviour. That is `x = 3` might behave different from `locals()['x'] = 3`. – skyking Jul 30 '15 at 09:54
  • @skyking thanks for pointing that out. Reworded to remove that, because it's not necessary for this to work; what needs to work is that *when a namespace dictionary is set,* setting a variable goes through that dictionary. – Dan Getz Jul 30 '15 at 11:31
1

This is an interesting idea; unfortunately, Python is not very restrictive and does not offer out-of-the-box solutions for such intentions. Overriding lower-level identifiers in deeper nested scopes is part of Python's philosophy and is wanted and often used, in fact. If you disabled this feature somehow, I guess a lot of library code would be broken at once.

Nevertheless you could create a check function which tests if anything in the current stack has been overridden. For this you would step through all the nested frames you are in and check if their locals also exist in their parent. This is very introspective work and probably not what you want to do but I think it could be done. With such a tool you could use the trace facility of Python to check after each executed line whether the state is still clean; that's the same functionality a debugger uses for step-by-step debugging, so this is again probably not what you want.

It could be done, but it would be like nailing glasses to a wall to make sure you never forget where they are.

Some more practical approach:

For builtins like the ones you mentioned you always can access them by writing explicitly __builtins__.set etc. For things imported, import the module and call the things by their module name (e. g. sys.exit() instead of exit()). And normally one knows when one is going to use an identifier, so just do not override it, e. g. do not create a variable named set if you are going to create a set object.

Alfe
  • 56,346
  • 20
  • 107
  • 159
  • Yes, accessible by `__builtins__.set` unless of course someone has assigned something else to `__builtins__` before (but that's worse because by then you'd hide `set` as well). There's seem to be no sacred identifiers in python. – skyking Jul 30 '15 at 09:51
  • @skyking While assigning to `set` seems relatively plausible in case someone doesn't know that `set` is already a used identifier, assigning to `__builtins__` seems so unusual that this then probably is done intentionally. – Alfe Jul 30 '15 at 11:10