簡體   English   中英

使用Hy宏生成Python代碼

[英]Generating Python code with Hy macros

我試圖從Hy生成一些python代碼。 怎么做得更好?

我嘗試了幾種方法。 一個是宏:

(defmacro make-vars [data]
  (setv res '())
  (for [element data]
    (setv varname (HySymbol (+ "var" (str element))))
    (setv res (cons `(setv ~varname 0) res)))
  `(do ~@res))

然后在捕獲宏擴展后,我打印python反匯編代碼。

但是,似乎使用宏我無法傳遞變量,因此:

(setv vnames [1 2 3])
(make-vars vnames)

定義varvvarnvara等,在代替var1var2var3 似乎可以通過以下方式進行正確的調用:

(macroexpand `(make-vars ~vnames))

但這似乎過於復雜。

我遇到的另一個問題是HySymbol的必要性,這是一個驚喜。 但是當我嘗試第二種方法時,我真的受到了傷害,在那里我創建了一個返回引用形式的函數:

(defn make-faction-detaches [faction metadata unit-types]
  (let [meta-base (get metadata "Base")
        meta-pattern (get metadata "Sections")
        class-cand []
        class-def '()
        class-grouping (dict)]
    (for [(, sec-name sec-flag) (.iteritems meta-pattern)]
      ;; if section flag is set but no unit types with the section are found, break and return nothing
      (print "checking" sec-name)
      (if-not (or (not sec-flag) (any (genexpr (in sec-name (. ut roles)) [ut unit-types])))
              (break)
              ;; save unit types for section 
              (do
               (print "match for section" sec-name)
               (setv sec-grouping (list-comp ut [ut unit-types]
                                             (in sec-name (. ut roles))))
               (print (len sec-grouping) "types found for section" sec-name)
               (when sec-grouping
                 (assoc class-grouping sec-name sec-grouping))))
      ;; in case we finished the cycle
      (else
       (do
        (def
          class-name (.format "{}_{}" (. meta-base __name__) (fix-faction-string faction))
          army-id (.format "{}_{}" (. meta-base army_id) (fix-faction-string faction))
          army-name (.format "{} ({})" (fix-faction-name faction) (. meta-base army_name)))
         (print "Class name is" class-name)
         (print "Army id is" army-id)
         (print "Army name is" army-name)
         (setv class-cand [(HySymbol class-name)])
         (setv class-def [`(defclass ~(HySymbol class-name) [~(HySymbol (. meta-base __name__))]
                            [army_name ~(HyString army-name)
                             faction ~(HyString faction)
                             army_id ~(HyString army-id)]
                             (defn --init-- [self]
                               (.--init-- (super) ~(HyDict (interleave (genexpr (HyString k) [k class-grouping])
                                                                       (cycle [(HyInteger 1)]))))
                               ~@(map (fn [key]
                                        `(.add-classes (. self ~(HySymbol key))
                                                       ~(HyList (genexpr (HySymbol (. ut __name__))
                                                                         [ut (get class-grouping key)]))))
                                      class-grouping)))]))))
    (, class-def class-cand)))

該函數在python中采用如下所示的元數據:

metadata = [
    {'Base': DetachPatrol,
     'Sections': {'hq': True, 'elite': False,
                  'troops': True, 'fast': False,
                  'heavy': False, 'fliers': False,
                  'transports': False}}]

並獲取具有以下形式的類的列表:

class SomeSection(object):
    roles = ['hq']

它需要廣泛使用hy的內部類,並且我沒有正確地表示True和False, HyInteger(1)HyInteger(0)

要從此函數獲取python代碼,我通過disassemble運行其結果。

總結一下:

  1. 從Hy生成python代碼的最佳方法是什么?
  2. 什么是真假的內部表示?
  3. 可以調用一個處理其參數的函數並從宏返回一個引用的Hy形式,以及如何?

在Hy中,你通常不需要生成Python代碼,因為Hy在生成Hy代碼方面要好得多,而且它就像可執行代碼一樣。 這一直是在Hy宏中完成的。

在不尋常的情況下,您需要生成真正的Python而不僅僅是Hy,最好的方法是使用字符串,就像在Python中一樣。 Hy編譯為Python的AST,而不是Python本身。 反匯編程序實際上只是用於調試目的。 它並不總是生成有效的Python:

=> (setv +!@$ 42)
=> +!@$
42
=> (disassemble '(setv +!@$ 42) True)
'+!@$ = 42'
=> (exec (disassemble '(setv +!@$ 42) True))
Traceback (most recent call last):
  File "/home/gilch/repos/hy/hy/importer.py", line 193, in hy_eval
    return eval(ast_compile(expr, "<eval>", "eval"), namespace)
  File "<eval>", line 1, in <module>
  File "<string>", line 1
    +!@$ = 42
     ^
SyntaxError: invalid syntax
=> (exec "spam = 42; print(spam)")
42

變量名+!@$和AST中的spam一樣合法,但是Python的exec會因為它不是有效的Python標識符而扼殺它。

如果您理解並且可以使用此限制,則可以使用disassemble ,但不使用宏。 允許普通的運行時函數獲取並生成(如您所示)Hy表達式。 宏實際上只是這樣的函數而不是在編譯時運行。 在Hy中,將一部分工作委托給一個普通函數並將Hy表達式作為其參數之一並返回Hy表達式並不罕見。

創建Hy表達式作為數據的最簡單方法是用'引用它。 內插值的反引號語法即使在宏的主體之外也是有效的。 也可以在普通的運行時函數中使用它。 但是要明白,如果要反匯編它,必須在插值中插入引用的形式 ,因為這就是宏作為參數接收的內容 - 代碼本身 ,而不是其評估值。 這就是你使用HySymbol和朋友的原因。

=> (setv class-name 'Foo)  ; N.B. 'Foo is quoted
=> (print (disassemble `(defclass ~class-name) True))
class Foo:
    pass

您可以詢問REPL它用於引用表單的類型。

=> (type 1)
<class 'int'>
=> (type '1)
<class 'hy.models.HyInteger'>
=> (type "foo!")
<class 'str'>
=> (type '"foo!")
<class 'hy.models.HyString'>
=> (type True)
<class 'bool'>
=> (type 'True)
<class 'hy.models.HySymbol'>

如您所見, True只是內部的符號。 請注意,我是能夠產生HySymbol' ,而不使用HySymbol通話。 如果您的元數據文件是用Hy編寫的,並且首先使用帶引號的Hy表單制作,則不必轉換它們。 但是沒有理由在倒鈎形式的最后一刻完成它。 如果這是你喜歡的,那么可以通過輔助函數提前完成。


跟進

可以調用一個處理其參數的函數並從宏返回一個引用的Hy形式,以及如何?

我原來的觀點是,宏是你正在嘗試做的錯誤工具。 但是要澄清一下,您可以在運行時通過使用宏macroexpand來調用宏,如您已經演示的那樣。 當然,您可以將macroexpand調用放在另一個函數中,但是macroexpand必須使用帶引號的形式作為其參數。

此外,關於動態生成的詞典的相同問題。 我用過的建築看起來很可怕。

字典部分可以簡化為更類似的東西

{~@(interleave (map HyString class-grouping) (repeat '1))}

雖然Python的dict由哈希表支持,但Hy的HyDict模型實際上只是一個列表。 這是因為它不代表哈希表本身,而是代表生成字典的代碼 這就是為什么你可以像列表一樣拼接它。

但是,如果可能的話,您是否可以添加一個將動態生成的字符串正確傳遞到最終引用表達式的示例? 據我了解,可以通過添加一個分配(這將添加引用)來完成,但是有更優雅的方式嗎?

Hy的模型被認為是公共API的一部分,它們在宏之外並沒有太多使用。 在需要時可以使用它們。 其他Lisps在代碼模型對象和它們產生的數據之間沒有相同的區別。 Hy這樣做是為了更好的Python互操作。 有人可能會說~語法應該針對某些數據類型自動執行此轉換,但是目前它沒有。 [ 更新 :在當前的主分支上,Hy的編譯器會盡可能自動包裝Hy模型中的兼容值,因此您通常不必自己執行此操作。]

HySymbol適用於動態生成字符串,就像您嘗試的那樣。 這不是唯一的方法,但在這種情況下,這就是你想要的。 另一種方式, gensym ,在宏中更經常使用,但它們不能那么漂亮。 您可以使用字符串調用gensym以為調試目的提供更有意義的名稱,但它仍然具有數字后綴以使其唯一。 當然,您可以為HySymbol分配一個較短的別名,或者將該部分委托給輔助函數。

您也可以提前轉換它,例如片段

(def class-name (.format "{}_{}" (. meta-base __name__) ...

可能相反

(def class-name (HySymbol (.format "{}_{}" (. meta-base __name__) ...

那你就不必兩次了。

(setv class-cand [class-name])
(setv class-def [`(defclass ~class-name ...

這可能使模板更容易閱讀。


更新

Hy master現在在編譯時將符號修改為有效的Python標識符,因此hy2py工具和astor反匯編應該更可靠地生成有效的Python代碼,即使符號中有特殊字符也是如此。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM