Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to prevent overwritting Python Built-in Function by accident?

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.)

like image 619
Atlas7 Avatar asked Jul 29 '15 12:07

Atlas7


2 Answers

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
like image 117
Dan Getz Avatar answered Sep 30 '22 03:09

Dan Getz


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.

like image 24
Alfe Avatar answered Sep 30 '22 03:09

Alfe