在HyLang中继承并实例化type.namedTuple的子类型

在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 
+0

可以在实际上建立在Python 3.6新的命名元组使用'typing.NamedTuple'虽然。请参阅[这里](https://docs.python.org/3/library/typing.html#typing.NamedTuple)。 – bjd2385

+2

@ bjd2385哦,这是一个新功能。它使用元类和类型注释。 Hy还没有支持Python的类型注释,所以我不认为这个等价物可以在Hy中工作。我会用解决方法更新答案。 – gilch

+0

@ bjd2385请参阅上面的解决方法 – gilch