0

我正在尝试使用 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.

(defclass Key [NamedTuple]
  (setv self.KEY KEY)
  (setv self.IDX IDX)
  (setv self.END END))

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

4

1 回答 1

2

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 中执行此操作的示例。

=> (setv spam (fn[]))
=> (setv spam.x 7)
=> spam.x
7

在上面的示例中,我x在一个空函数对象上设置了一个属性。有些对象虽然没有属性 dict。您可以使用__slots__语法自己创建此类对象。(有关其工作原理,请参阅Python 文档。)

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

=> (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

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

A HySymbolis-a Python 字符串(子类),在大多数情况下工作方式相同。这:G_1235是一个 Hy gensym 名称。这些不是 Python 有效的标识符,但 Hy 编译为 Python 抽象语法树,并且 AST 将接受这样的名称。您可以亲自了解 Hy 如何使用 AST 本身或其近似 Python 等效项的--spy选项或函数来编译 repl 中的内容。(disassemble ...)

您还可以NamedTuple通过分配给您在类主体中使用 注释的名称来提供默认值setv


如果您使用的是 Python 3.6+(并且您将获得NamedTuple元类),那么您可以使用 kwargs 来制作OrderedDict,因为PEP 468。不要OrderedDict在不保证 kwargs 顺序的早期版本中使用这种方式。

在海,

(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
于 2017-08-10T21:30:14.913 回答