2012-09-18 28 views
4

在閱讀with聲明(link)的規範時,我有一些我想玩的東西。這不適用於任何生產代碼或任何東西,我只是在探索,所以如果這是一個壞主意,請不要太苛刻。獲取要在語句中執行的命令塊

我想要做的是在上面的鏈接文檔中抓取名爲「BLOCK」的作品,並在__enter__的調用中實際修補它。 (請參閱鏈接文檔,在激勵和摘要部分開始之後)。

這個想法是創建我自己的即時本地命名空間。事情是這樣的:

with MyNameSpace(some_object): 
    print a #Should print some_object.a 
    x = 4 #Should set some_object.x=4 

基本上,我想with塊中的語句是從屬於局部變量和some_object分配慣例。

在我的具體情況下,some_object可能是一個特殊的數據陣列,它具有我自己的按列操作或其他內容。在這種情況下,像x = y + 5 if y > 4 else y - 2這樣的內容可能是一些奇妙的NumPy向量化操作,但我不需要明確地調用some_object的這些方法的接口。在命名空間中,表述應該「只是工作」(但是我確定他們在MyNameSpace類來推斷。

我的第一個想法是莫名其妙中斷with過程和獲取雲在try代碼保持然後在調用__enter__時解釋該代碼,並用其他內容替換try塊中的代碼(如果可以,則可能爲pass,但可能會將some_object恢復爲原始變量作用域並保留其新的已更改變量)。

一個簡單的測試案例會是這樣的:

my_dict = {'a':3, 'b':2} 
with MyNameSpace(my_dict): 
    print a # Should print 3 
    x = 5 # When the block finishes, my_dict['x'] should now be 5 

我很感興趣,如果這個想法已經存在的地方。

我知道分配變量的最佳做法。這是一個寵物項目,所以請假定,僅僅爲了這個想法,我們可以忽略最佳實踐。即使你不喜歡用這種方式分配變量,它也可能對我當前的項目有用。

編輯

爲了澄清我可能要做好各種棘手的問題,並解決以下聲稱它不能做的答案,可以考慮下面的示例文件testLocals.py

my_dict = {'a':1, 'b':2} 
m = locals() 
print m["my_dict"]['a'] 
m["my_dict"]['c'] = 3 
print my_dict 

class some_other_scope(object): 
    def __init__(self, some_scope): 
     x = 5 
     g = locals() 
     some_scope.update(g) 
     some_scope["my_dict"]["d"] = 4 

sos = some_other_scope(m) 
print my_dict 
print x 

這給當我運行它非交互如下:

[email protected]:~/Desktop/Programming/Python$ python testLocals.py 
1 
{'a': 1, 'c': 3, 'b': 2} 
{'a': 1, 'c': 3, 'b': 2, 'd': 4} 
5 
+0

你可以使用'with'而不指定(使用'with x as f'或'with x = f')? –

+0

是的,它只是調用上下文管理器的'__enter__'函數等。如果你沒有在塊內引用'with'後面的東西,那麼它就不需要那個東西的名字。 – ely

+0

你可以用當前棧幀中的'globals'字典來猴子,但最根本的問題是它不能在函數內部工作。當一個函數被編譯爲字節碼並以流線形式處理局部變量以獲得更好的性能時,Python會靜態地確定哪些變量是本地變量。 – kindall

回答

5

試試這個。

import sys 
class MyNameSpace(object): 
    def __init__(self,ns): 
     self.ns = ns 
    def __enter__(self): 
     globals().update(self.ns) 
    def __exit__(self, exc_type,exc_value,traceback): 
     self.ns.update(sys._getframe(1).f_locals) 

my_dict = {'a':3, 'b':2} 
with MyNameSpace(my_dict) as ns: 
    print(a) # Should print 3 
    x = 5 # When the block finishes, my_dict['x'] should now be 5 

print(my_dict['x']) 
2

下面是一些s^imilar我嘗試了幾個月前:

import sys 
import inspect 
import collections 

iscallable = lambda x: isinstance(x, collections.Callable) 

class Namespace(object): 
    def __enter__(self): 
     """store the pre-contextmanager scope""" 
     f = inspect.currentframe(1) 
     self.scope_before = dict(f.f_locals) 
     return self 

    def __exit__(self, exc_type, exc_value, traceback): 
     """determine the locally declared objects""" 
     f = inspect.currentframe(1) 
     scope_after = dict(f.f_locals) 
     scope_context = set(scope_after) - set(self.scope_before) 

     # capture the local scope, ignoring the context manager itself 
     self.locals = dict(
      (k, scope_after[k]) for k in scope_context if not isinstance(scope_after[k], self.__class__) 
     ) 

     for name in self.locals: 
      obj = scope_after[name] 
      if iscallable(obj): 
       # closure around the func_code with the appropriate locals 
       _wrapper = type(lambda: 0)(obj.func_code, self.locals) 
       self.__dict__[name] = _wrapper 
       # update locals so the calling functions refer to the wrappers too 
       self.locals[name] = _wrapper 
      else: 
       self.__dict__[name] = obj 

      # remove from module scope 
      del sys.modules[__name__].__dict__[name] 

     return self 

with Namespace() as Spam: 
    x = 1 
    def ham(a): 
     return x + a  
    def cheese(a): 
     return ham(a) * 10 

它使用檢查修改當地人而上下文管理器中,然後在完成時重新分配回原來的值。

這並不完美 - 我不記得它遇到問題的位置,但我確信它確實存在 - 但它可以幫助您入門。

+0

這隻允許您添加或刪除'locals()'中的值,這些值在'with'套件中保持靜態。 ''locals()'映射適用於'with'套件之後的所有代碼都使用相同的本地名稱空間,因此您需要小心清理。 –

+0

是的,這不是一個完美的方法,但它確實有點面向所要求的問題。它旨在通過引用命名空間來使用,而不是僅僅存在於上下文管理器的生命週期中,因此它試圖將所有可調用對象封裝在閉包中。 (其實,在重讀這個問題時,我發現我錯誤地理解了所要求的內容,Odomontious的回答是一個更好的方法。) –