什么时候可以接受隐藏内置函数?

2e0*_*byo -1 python

我们经常被告知不要隐藏诸如idintlist内建函数。打破这条规则可以接受吗?

2e0*_*byo 5

一般来说,出于以下几个原因,您不应该隐藏内置函数:

  • 这样做可以防止您稍后通过调用其通用名称来使用内置函数
  • 其他阅读你的代码的人会感到困惑,因为他们期望list成为内置的list,等等
  • IDE(通常)会通过突出显示隐藏的变量来复合这一点,就好像它们是内置变量一样。

但也有一些例外。

在类属性中

在类属性中使用内置名称在运行时根本不会隐藏任何内容,尽管在评估时可能会隐藏任何内容。这种模式很常见:

class Foo:
    def __init__(self, foo_id: int) -> None:
        self.id = foo_id
Run Code Online (Sandbox Code Playgroud)

Foo().id直到我们在构造函数中绑定它才被绑定。这里没有发生阴影。使用 来id表示“身份”是非常常见的,它可能比任何替代方案(如obj_ididentity)更好,并且没有人会期望Foo().id成为内置的id

如果您使用数据类,这可能涉及阴影:

@dataclass
class Foo:
    # id refers to the builtin `id`
    id: int = 0 # assigning a default for the example
    # id referes to `Foo.id`, which has the typehint int

foo = Foo(id="id of foo")
# foo.id refers to the *instance* id, which is actually a string.
Run Code Online (Sandbox Code Playgroud)

在这个例子中,我们简单地做了shadow id。我们无法在评估时使用它。但无论如何,在定义(而不是构造)类时,您可能不应该获取对象的 id。由于数据类的工作方式,我们最终id在实例上得到了一个单独的实例foo(如此处所示,甚至不必是正确的类型!)。共有三个命名空间在起作用:封闭命名空间(这id意味着它通常所做的事情)、未实例化类的命名空间Foo(其中id是 0)和对象的命名空间foo(其中id是字符串)。我们已经在第二个中进行了隐藏,但无论如何我们稍后不会在该名称空间中执行任何操作。

在小函数中,当需要清晰度时

如果这样做可以提高清晰度,那么对一个小函数中的内置函数进行隐藏可能是可以接受的,因为该函数永远无法使用被隐藏的对象。这两个可能都很好:

def get_object_from_db(id: int) -> CustomObj:
    ...

def find_enclosing_dir(filename: str) -> Pathlib.Path:
    f = _find_file(filename)
    dir = f.parent
    some_logic_to_validate(dir)
    return dir

class Foo:
    def __init__(self, id: int) -> None:
        self.id = id

Run Code Online (Sandbox Code Playgroud)

第一个示例是id函数中的阴影,其中我们关心的唯一“id”是我们正在检索以构建CustomObj. 不过,obj_id在这里使用可能会更清楚。第二个示例隐藏dir并将其用于目录。 dir并且类似的自省工具不太可能在这段代码中使用;再次parent_dir 可能会更清楚,但这里的阴影是一种风格选择。在第三个示例中,我们id在 for 的构造函数中进行隐藏Foo。同样,我们现在不能使用id,但我们的代码可以说更干净。

三思而后行

一般来说,避免阴影是一件好事。在小函数或方法中,为了清晰起见,隐藏内置函数可能是有意义的。只有当它增加清晰度时才应该这样做,并且如果发生一些变化而进行重构以避免阴影的工作量很小。至少该功能应该能够舒适地显示在一个屏幕上。类属性可以不受惩罚地“隐藏”(因为它们根本不隐藏),但只有少数名称可以像这样使用。 id或许dir有道理,但Foo.listorFoo.str可能不如描述性名称那么清晰。