在HyLang中继承并实例化type.namedTuple的子类型
我正在尝试使用Hy,这是一个建立在Python之上的Lisp方言。在HyLang中继承并实例化type.namedTuple的子类型
我试着运行以下内容,但是,正如所料,我得到了一个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类中设置字段变量?
来自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
。尽管如此,一些对象没有属性字典。您可以使用__slots__
语法自己创建这些对象。 (请参见Python documentation了解它的工作原理。)
在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
海兰还没有对Python的类型注释的语法。 NamedTuple
元类使用这些。在某些情况下,您可以通过自己创建__annotations__
字典来解决此问题。
(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
,但这是最直接的之一。我们需要订购注释字典,因为订购了NamedTuple
。
A HySymbol
是一个Python字符串(子类),在大多数情况下都是一样的。 :G_1235
是Hy gensym的名字。这些不是Python有效的标识符,但Hy编译为Python抽象语法树,AST将接受类似的名称。你可以亲自看看Hy是如何编译repl的,--spy
选项或(disassemble ...)
函数用于AST本身,或者它的近似Python等价物。
您还可以通过指派给您在课程正文注释的名称setv
来为NamedTuple
提供默认值。
如果你对Python的3.6+(你会被给予NamedTuple
元类),那么你可以使用kwargs作出OrderedDict
,由于PEP 468。在不保证kwargs顺序的早期版本中,不要使用OrderedDict
。
在海兰,
(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
可以在实际上建立在Python 3.6新的命名元组使用'typing.NamedTuple'虽然。请参阅[这里](https://docs.python.org/3/library/typing.html#typing.NamedTuple)。 – bjd2385
@ bjd2385哦,这是一个新功能。它使用元类和类型注释。 Hy还没有支持Python的类型注释,所以我不认为这个等价物可以在Hy中工作。我会用解决方法更新答案。 – gilch
@ bjd2385请参阅上面的解决方法 – gilch