简体   繁体   English

Python:在函数内命名类型并在函数外分配变量

[英]Python: Naming types and assigning variables outside a function from within a function

I'd like to be able to easily create new types, plus (optionally) add some information about them (say some docs, and a set of variable names they often come under).我希望能够轻松创建新类型,加上(可选)添加一些关于它们的信息(比如一些文档,以及它们经常出现的一组变量名称)。

The straightforward way to do this would be:这样做的直接方法是:

from typing import Any, NewType, Union, List, Iterable, Optional

Key = NewType('Key', Any)
Key._aka = set(['key', 'k'])

Val = NewType('Val', Union[int, float, List[Union[int, float]]])
Val.__doc__ = "A number or list of numbers."

But there's two reasons I don't like this:但我不喜欢这样的原因有两个:

  • I have to copy paste the name of the new type I'm making three times (not DRY and prone to mistakes)我必须复制粘贴我正在制作的新类型的名称三遍(不是 DRY 并且容易出错)

  • I don't like to "externalize" the assignment of optional additional information ( _aka and __doc__ )我不喜欢“外部化”可选附加信息( _aka__doc__ )的分配

So I came up with this:所以我想出了这个:

from typing import Any, NewType, Union, List, Iterable, Optional

def new_type(name, tp, doc: Optional[str]=None, aka: Optional[Iterable]=None):
    """Make a new type with (optional) doc and (optional) aka, set of var names it often appears as"""
    new_tp = NewType(name, tp)
    if doc is not None: 
        setattr(new_tp, '__doc__', doc)
    if aka is not None: 
        setattr(new_tp, '_aka', set(aka))
    globals()[name] = new_tp  # is this dangerous? Does scope need to be considered more carefully?

which then gives me the interface I'd like:然后给了我我想要的界面:

new_type('Key', Any, aka=['key', 'k'])
new_type('Val', Union[int, float, List[Union[int, float]]], doc="A number or list of numbers.")

But I'm not sure of that globals()[name] = new_tp thing.但我不确定globals()[name] = new_tp事情。 It seems it would be benign if I'm defining my types in the top level of a module, but not sure how this would fair in some edge case nested scopes situation.如果我在模块的顶层定义我的类型,这似乎是良性的,但不确定在某些边缘情况嵌套范围情况下这如何公平。

The normal way you create a new type is to just write a new class:创建新类型的正常方法是编写一个新类:

class Key:
    def __init__(self, key: object) -> None:
        self._key = key
        self._aka = set(['key', 'k'])

class SqlString(str):
    """A custom string which has been verified to be valid SQL."""

Note that this approach avoids the DRY and scoping concerns that you had.请注意,此方法可避免您遇到的 DRY 和范围界定问题。

You use NewType only for when you don't want to add any extra attributes or a docstring -- doing Foo = NewType('Foo', X) is basically like doing class Foo(X): pass except with slightly less runtime overhead.您可以使用NEWTYPE只有当你希望添加任何额外的属性或文档字符串-做Foo = NewType('Foo', X)基本上是像做class Foo(X): pass除了具有略低于运行时开销。

(More precisely, type checkers will treat Foo = NewType('Foo', X) as if it were written like class Foo(X): pass , but at runtime what actually happens Foo = lambda x: x -- Foo is the identity function.) (更准确地说,类型检查器会将Foo = NewType('Foo', X)视为像class Foo(X): pass那样编写,但在运行时实际发生的事情Foo = lambda x: x -- Foo 是身份功能。)


We do run into a complication with your second Union example, however, since Unions are not subclassable (which makes that NewType illegal, as per PEP 484 ).但是,我们确实遇到了您的第二个 Union 示例的复杂情况,因为 Unions 不可子类化(这使得 NewType 非法,根据PEP 484 )。

Instead, I would personally just do the following:相反,我个人只会执行以下操作:

# A number or list of numbers
Val = Union[int, float, List[Union[int, float]]]

IMO since types are supposed to be invisible at runtime, I think it makes sense to just not bother attaching runtime-available documentation. IMO 因为类型应该在运行时不可见,所以我认为不费心附加运行时可用的文档是有意义的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM