1

What could possibly go wrong if I unintentionally overwrite/mask build-in function in Python?

Can I experience anything worse than the obvious pitfall of accessing a local function instead of a built-in function?

For example:

def max(a, b):
  pass

class MyCompileTool(object):
    def __init__(self, id):
        self.id = id

    def compile(self):
        min = "3.4.4"
        ...

Even in some official modules: argparse.add_argument(..., type, ...)

Peter Petrik
  • 9,701
  • 5
  • 41
  • 65
  • There are many possible scenarios, all basing on a fact that someone/you will not now/forget about this and will try to call these functions. This questions seems too broad. Also, note that in your example you are not modifying built-ins, just create local variables that shadow the built-ins only in the scope of the `compile` method. – BartoszKP Apr 22 '15 at 13:11
  • Isn't it obvious?! You could accidentally call the wrong function, and either get an error or (worse) quietly get the wrong result. Note that **overwriting** built-ins is (intentionally) very difficult to do by accident, it's **shadowing** them that you need to watch out for. Tools like `pylint` will warn you of this. Also avoid wildcard imports (`from foo import *`) for the same reason. – jonrsharpe Apr 22 '15 at 13:17
  • 2
    I feel like the OP is actually asking, "besides the obvious pitfall of not being able to access that function anymore, are there any consequences, such as 'internal Python functions that use `max` will get confused and crash when they can't find the usual implementation'?". To which the answer is, IIRC, "no". – Kevin Apr 22 '15 at 13:20
  • @Kevin ah, possibly - is that the case, OP? Is this really a question about the scope of names in Python? – jonrsharpe Apr 22 '15 at 13:25
  • possible duplicate of [How bad is shadowing names defined in outer scopes?](http://stackoverflow.com/questions/20125172/how-bad-is-shadowing-names-defined-in-outer-scopes) – bruno desthuilliers Apr 22 '15 at 13:26
  • @Kevin I have been told that it is a bad practise and now I want to undestand how bad it is to use it. More or less the answer/question you have formulated in your comment. – Peter Petrik Apr 22 '15 at 13:35

2 Answers2

4

You do not overwrite builtin min here, you just create a min local, which will be preferred if subsequent code of compile will contain calls to min:

class MyCompileTool(object):
    ...

    def compile(self):
        min = "3.4.4"
        x = min(1, 2)
        #   ^^^ "3.4.4".__call__(1, 2)
        #   This will throw exception because strings doesn't have __call__
x = min(3, 4)
#   ^^^ __builtins__.min

To shadow min in entire module, do that in global namespace:

min = "3.4.4"
# Now all calls of min will go to that string

class MyCompileTool(object):
     pass

For more information on how names are resolved in Python, check documentation: 4.1. Naming and binding

BartoszKP
  • 34,786
  • 15
  • 102
  • 130
myaut
  • 11,174
  • 2
  • 30
  • 62
  • About the word "*completely*": Wouldn't any call to `min()` in an imported module still reference the regular `min()`? I am not too sure how those rules work... – Hannes Ovrén Apr 22 '15 at 13:18
  • @HannesOvrén, thanks for that note, you absolutely correct -- it is done in global namespace of single module. I have my answer fixed. – myaut Apr 22 '15 at 13:22
  • @myaut "single module", unless another module will do `from x import *`. – BartoszKP Apr 22 '15 at 13:22
  • 1
    @BartoszKP: if we count that as a syntactic sugar for `import x; min = x.min; ...`, than it is no different than my example ;) – myaut Apr 22 '15 at 13:25
0

In this particular situation, there probably isn't much that can/will go wrong. You're just shadowing min with a local variable in your method, so you're probably not going to do much damage. In general though, you could see something like this:

>>> min = "3.4.4"
>>> x = min(3, 4, 5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object is not callable
>>>

This might confuse you or (even worse) it might confuse the person who has to go in and support your code later. It would be even worse if you did it globally, like:

min = "3.4.4"

def main():
    x = min(3, 4, 5)

Overall, it's bad practice. Don't do it.

Deacon
  • 3,615
  • 2
  • 31
  • 52