2017-08-10 71 views
0

我正在嘗試使用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類中設置字段變量?

回答

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。儘管如此,一些對象沒有屬性字典。您可以使用__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