继承和实例化的子类型键入.NamedTuple在海朗

2024-09-22 16:30:30 发布

您现在位置:Python中文网/ 问答频道 /正文

我尝试使用Hy,这是在Python之上构建的Lisp方言。在

我试着运行下面的代码,但是正如预期的那样,我得到了一个AttributeError: Cannot overwrite NamedTuple attribute __init__。在

(defclass Key [NamedTuple]
  ;; Simple container for holding keywords
  (defn --init-- [self KEY IDX END]
    (setv self.KEY KEY)
    (setv self.IDX IDX)
    (setv self.END END)))

另一方面,我不知道在类中定义字段变量要使用哪种语法。我尝试了以下方法,但它引发了一个NameError: name 'KEY' is not defined。在

^{pr2}$

那么,如何在Lispy/Python类中设置字段变量呢?在


Tags: key代码selfinitattributenamedtupleendattributeerror
1条回答
网友
1楼 · 发布于 2024-09-22 16:30:30

在 Python标准库中的命名元组不需要类声明。使用函数调用创建类。在

=> (import [collections [namedtuple]])
=> (namedtuple 'Point3D '[x y z])
<class '__console__.Point3D'>
=> (setv Point3D _)  ; Hy's repl sets _ to the previous result.
=> (Point3D 1 2 3)
Point3D(x=1, y=2, z=3)

命名元组实例是不可变的,就像普通的Python元组一样。你不能分配给他们。在

Python允许在大多数对象上设置任意属性。下面是在Hy中这样做的一个例子

^{pr2}$

在上面的例子中,我在一个空函数对象上设置了一个属性x。但是有些对象没有dict属性。您可以使用__slots__语法自己创建这样的对象。(请参阅Python documentation以了解其工作原理。)

在Python(和Hy)中,^{不存在于类声明中,只存在于方法中,因为它是第一个参数。这就是为什么你不能分配给它。您可以直接setv一个名称,但这将放入dict类中,而不是在任何特定实例中。在

=> (defclass Foo []
... (setv class-foo 7)  ; lives in the class dict
... (defn __init__ [self foo]
...   (setv self.foo foo)))  ; lives in the instance dict
=> Foo.class-foo
7
=> (. (Foo 12) foo)  ; the (.) form accesses attributes.
12
=> (. (Foo 12) class-foo)  ; not in the instance, so look in its class
7

Hy还没有Python类型注释的语法。NamedTuple元类使用这些。在某些情况下,您可以自己创建__annotations__dict来解决这个问题。在

(import [collections [OrderedDict]]
        [typing [NamedTuple]])

(defclass Key [NamedTuple]
  (setv (get (vars) '__annotations__)
        (doto (OrderedDict)
              (assoc 'KEY KEY
                     'IDX IDX
                     'END END))))

这应该和Python一样工作

class Key(NamedTuple):
    KEY: KEY
    IDX: IDX
    END: END

虽然它实际上编译成

class Key(NamedTuple):
    :G_1235 = OrderedDict()
    :G_1235[HySymbol('KEY')] = KEY
    :G_1235[HySymbol('IDX')] = IDX
    :G_1235[HySymbol('END')] = END
    vars()[HySymbol('__annotations__')] = :G_1235

在Hy中还有其他创建OrderedDict的方法,但这是最直接的方法之一。我们需要对注释dict进行排序,因为NamedTuple是有序的。在

HySymbol是一个Python字符串(子类),在大多数上下文中都是一样的。:G_1235是一个Hy gensym名称。这些不是Python有效的标识符,但是Hy编译成Python抽象语法树,AST将接受这样的名称。您可以亲眼看到Hy如何使用 spy选项或(disassemble ...)函数为AST本身或其近似的Python等价物编译repl中的内容。在

您还可以为NamedTuple提供一个默认值,方法是为在类主体中用setv注释的名称赋值。在


如果您使用的是python3.6+(您将得到NamedTuple元类),那么您可以使用kwargs生成一个OrderedDict,这是由于PEP 468。在不保证kwargs顺序的早期版本中,不要用这种方式生成OrderedDicts。在

在Hy

(defclass Foo [NamedTuple]
  (setv (get (vars) '__annotations__)
        (OrderedDict :name str
                     :ID int)
        name "foo"
        ID 42))

注意,一个setv可以分配多个对。最后两对被元类用作NamedTuple默认值。在

在repl

=> (Foo)
Foo(name='foo', ID=42)
=> (Foo "bar")
Foo(name='bar', ID=42)
=> _.ID
42

相关问题 更多 >