Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it OK to re-use the names of Python's built-in functions?

As currently being discussed in this question, I am writing inclusive versions of the built-in range and xrange functions. If these are placed within a module named inclusive, I see two possible ways to name the functions themselves:

  1. Name the functions inclusive_range and inclusive_xrange so that client code can use them as follows:

    from inclusive import *
    ...
    inclusive_range(...)
    
  2. Name the functions range and xrange:

    import inclusive
    ...
    inclusive.range(...)
    

To me, the second example of client code looks better, but should I avoid re-using built-in names in this way?

like image 355
user200783 Avatar asked Sep 29 '22 06:09

user200783


1 Answers

This turned into a bit of a list of different options, rather than a straight answer. However, two concepts are paramount:

  • Users should be able to replace range and xrange if they explicitly choose to; but
  • Users should not be able to implicitly/accidentally replace range and xrange;

it should always be clear to readers of their code where the built-ins are used and where the replacements are used.

For that reason, all options I've outlined below prevent a wildcard import (from inclusive import *) from shadowing the built-ins. Which is the best option for you depends on whether you see replacing the built-ins as a primary or secondary use of your module. Will users generally want to replace the built-ins, or use them alongside each other?


In your position, I think I would do the following:

inclusive.py:

"""Inclusive versions of range and xrange."""

__all__ = []  # block 'from inclusive import *'

old_range, old_xrange = range, xrange  # alias for access

def range(...):  # shadow the built-in
    ...

def xrange(...):  # ditto
    ...

This allows users to either:

  1. import inclusive and access inclusive.range and inclusive.xrange;
  2. from inclusive import range, xrange, clearly replacing the built-ins without any unpleasant side effects; or
  3. from inclusive import range as irange, xrange as ixrange to use the built-in and replacement versions alongside one another.

Defining __all__ as an empty list means that from inclusive import * won't quietly shadow the built-ins.


If you really wanted to, you could add:

irange, ixrange = range, xrange

to the end of inclusive.py and modify the __all__ definition to:

__all__ = ['irange', 'ixrange']

Now the users have two additional choices:

  • from inclusive import irange, ixrange (slightly simpler than manually aliasing the functions as in option 3 above); and
  • from inclusive import * (same result as above, still no implicit shadowing of built-ins).

Of course, you could go completely the other way - name your own versions irange and ixrange, then if the user really wants to replace the built-ins they would have to:

from inclusive import irange as range, ixrange as xrange

This doesn't require you to define __all__ to avoid a wildcard import shadowing the built-ins.

like image 79
jonrsharpe Avatar answered Oct 06 '22 02:10

jonrsharpe