2017年9月27日 星期三

[ Python 常見問題 ] Detect re (regexp) object in Python

Source From Here 
Question 
I wonder what is the proper pythonic backward- and forward-compatible method how check if an object is compiled re object. isinstance method cannot be easily used, while the resulting object claims to be _sre.SRE_Patternobject: 
>>> import re 
>>> rex = re.compile('') 
>>> rex 
<_sre .sre_pattern="" 0x7f63db414390="" at="" object="">


How-To 
re._pattern_type exists, and appears to do what you want: 
>>> isinstance(re.compile(''), re._pattern_type) 
True

But this is not a good idea - per Python convention, names starting with _ are not part of the public API of a module and not part of the backward compatibility guarantees. So, using type(re.compile('')) is your best bet: 
>>> isinstance(re.compile('test'), type(re.compile('test'))) 
True 
>>> isinstance('test', type(re.compile('test'))) 
False

Though notice that this isn't guaranteed to work either, since the re module makes no mention of the object returned from re.compile() being of any particular class. And indeed, even if this was guaranteed, the most Pythonic and back- and forward- compatible way would be to rely on the interface, rather than the type. In other words, embracing duck typing and EAFP (It’s Easier to Ask Forgiveness than Permission), do something like this: 
  1. try:  
  2.      rex.match(my_string)  
  3. except AttributeError:  
  4.      # rex is not an re  
  5. else:  
  6.      # rex is an re  
Or another useful approach given you only care about using function 'match' (If a animal make sound of quack, it is possibly a duck...:p): 
>>> ptn = re.compile('test') 
>>> str = 'string' 
>>> hasattr(ptn, 'match') and callable(ptn.match) // If object 'ptn' has attribute 'match' and it is callable 
True 
>>> hasattr(str, 'match') and callable(str.match) 
False


2017年9月12日 星期二

[ Python 文章收集 ] 深入理解 Python 中的 __builtin__ 和 __builtins__

Source From Here 
Preface 
這裡的說明主要是以 Python 2.7 為例,因為在 Python 3+ 中,__builtin__ 模塊被命名為 builtins,下面主要是探討 Python 2.x 中 __builtin__ 模塊和 __builtins__ 模塊的區別和聯繫。 

名稱空間(Namespace) 
首先不得不說 名稱空間,因為名稱空間是 Python 中非常重要的一個概念,所謂名稱空間,其實指的是名稱(標識符)到對象的映射。在一個正常的Python程序的執行過程中,至少存在兩個名稱空間: 
* 內建名稱空間 
* 全局名稱空間

如果定義了函數,則還會有 局部名稱空間,全局名稱空間一般由在程序的全局變量和它們對應的映射對象組成,而局部名稱空間則在函數內部由函數局部變量和它們對應的映射對象組成,這里關鍵的是內建名稱空間,它到底是怎麼產生的? 

內建函數 
在啟動 Python 解釋器之後,即使沒有創建任何的變量或者函數,還是會有許多函數可以使用,比如: 
>>> abs(-1) 
1 
>>> max(1, 3) 
3

我們把這些函數稱為 內建函數,是因為它們不需要我們程序員作任何定義,在啟動 Python 解釋器的時候,就已經導入到內存當中供我們使用: 
>>> abs 
 
>>> max 

內建名稱空間與 __builtins__ 
那麼內建函數也是函數,雖然我們沒有人為導入這些,但是正如前面所說,在啟動 Python 解釋器的時候,會自動幫我們導入,那麼內建函數存在於哪裡呢?其實準確地來說,是 Python 解釋器在啟動的時候會首先加載內建名稱空間,內建名稱空間有許多名字到對象之間映射,而這些名字其實就是內建函數的名稱,對象就是這些內建函數本身(注意區分函數名稱和函數對象的區別)。這些名稱空間由 __builtins__ 模塊中的名字構成: 
>>> dir() 
['__builtins__', '__doc__', '__name__', '__package__']

可以看到有一個 __builtins__ 的模塊名稱,這個模塊本身定義了一個名稱空間,即內建名稱空間,我們不妨 dir 一下: 
>>> dir(__builtins__) 
['ArithmeticError', 'AssertionError', 'AttributeError', ...'vars', 'xrange', 'zip']

會看到我們熟悉的內建函數的名稱,如 list、dict 等,當然還有一些異常和其它屬性。 

__builtins__ 與 __builtin__ 的簡單區別 
既然內建名稱空間由 __builtins__ 模塊中的名稱空間定義,那麼是不是也意味著內建名稱空間中所對應的這些函數也是在 __builtins__ 模塊中實現的呢?顯然不是的,我們可以在解釋器中直接輸入 __builtins__ 
>>> __builtins__ 
__builtin__
' (built-in)>
從結果中可以看到,__builtins__ 其實還是引用了 __builtin__ 模塊而已,這說明真正的模塊是 __builtin__,也就是說,前面提到的內建函數其實是在內建模塊 __builtin__ 中定義的,即 __builtins__ 模塊包含內建名稱空間中內建名字的集合(因為它引用或者說指向了 __builtin__ 模塊),而真正的內建函數、異常和屬性來自 __builtin__ 模塊。也就是說,在 Python 中,其實真正是只有 __builtin__ 這個模塊,並不存在 __builtins__ 這個模塊 
>>> import __builtin__ 
>>> import __builtins__ 
Traceback (most recent call last): 
File "", line 1, in  
ImportError: No module named __builtins__

可以看到,導入 __builtin__ 模塊並沒有問題,但導入 __builtins__ 模塊時就會提示不存在,這充分說明了前面的結論,現在再次總結如下: 
在 Python 中並沒有 __builtins__ 這個模塊,只有 __builtin__ 模塊,__builtins__ 模塊只是在啟動 Python 解釋器時,解釋器為我們自動創建的一個到 __builtin__ 模塊的引用.

當然,至於這種引用到底是怎麼樣,可以看下面的深入區別。 

__builtins__ 與 __builtin__ 的深入區別 
上面只是大概說了下 __builtins__  __builtin__ 兩個模塊的簡單區分而已,其實深究下去,要分成下面所提及的兩種情況。 

在主模塊 __main__ 中 
其實我們在使用 Python 交互器的時候就是在主模塊中進行操作,可以做如下驗證: 
>>> print __name__ 
__main__

在這種情況,__builtins__  __builtin__ 是完全一樣的,它們指向的都是 __builtin__ 這個內建模塊: 
>>> import __builtin__ 
>>> __builtin__ 
 
>>> __builtins__ 
 
>>> __builtin__.__name__ 
'__builtin__' 
>>> __builtins__.__name__ 
'__builtin__' 
>>> __builtins__ == __builtin__ 
True 
>>> __builtins__ is __builtin__ 
True 
>>> id(__builtins__) 
140295127423752 
>>> id(__builtin__) 
140295127423752

可以看到,這時候 __builtins__  __builtin__ 是完全一樣的,它們都指向了同一個模塊對象,其實這也是Python中引用傳遞的概念。其實這種情況跟我們創建一個變量並對它做一次引用傳遞時的情況是一樣的,可以做如下測試: 
>>> def func(): 
... return 'test' 
... 
>>> func 
 
>>> funcs = func 
>>> funcs 
 
>>> func.__name__ 
'func' 
>>> funcs.__name__ 
'func' 
>>> funcs == func 
True 
>>> funcs is func 
True 
>>> id(funcs) 
140077021549944 
>>> id(func) 
140077021549944

顯然,這完全驗證了我們上面的結論。 

不是在主模塊中 
如果不是在主模塊中使用 __builtins__,這時候,__builtins__ 只是對 __builtin__.__dict__ 的一個簡單引用而已,可以通過下面的測試來驗證說明。先創建一個 test.py 模塊,後面我們需要在 Python 交互器中導入它,那麼這時候對於 test 模塊來說,它就不是主模塊了。如下: 
- test.py 
  1. #!/usr/bin/env python  
  2.   
  3. import __builtin__  
  4.   
  5.   
  6. print 'Module name:', __name__  
  7.   
  8.   
  9. print '*==test __builtin__ and __builtins__==*'  
  10. print '__builtin__ == __builtins__', __builtin__ == __builtins__  
  11. print '__builtin__ is __builtins__', __builtin__ is __builtins__  
  12. print 'id(__builtin__)', id(__builtin__)  
  13. print 'id(__builtins__)', id(__builtins__)  
  14.   
  15. print '='*50  
  16.   
  17. print '*==test __builtin__.__dict__ and __builtins__==*'  
  18. print '__builtin__.__dict__ == __builtins__', __builtin__.__dict__ == __builtins__  
  19. print '__builtin__.__dict__ is __builtins__', __builtin__.__dict__ is __builtins__  
  20. print 'id(__builtin__.__dict__)', id(__builtin__.__dict__)  
  21. print 'id(__builtins__)', id(__builtins__)  
在 Python 交互器中導入上面這個 test 模塊,如下: 
>>> import test 
Module name: test 
*==test __builtin__ and __builtins__==* 
__builtin__ == __builtins__ False 
__builtin__ is __builtins__ False 
id(__builtin__) 140592847690504 
id(__builtins__) 140592847925608 
================================================== 
*==test __builtin__.__dict__ and __builtins__==* 
__builtin__.__dict__ == __builtins__ True 
__builtin__.__dict__ is __builtins__ True 
id(__builtin__.__dict__) 140592847925608 
id(__builtins__) 140592847925608

可以看到輸出的結果跟我們想的是完全一樣的,即這時候 __builtins__ 其實是對 __builtin__.__dict__ 模塊的引用。 

總結 
不管怎麼說,在啟動 Python 解釋器或運行一個 Python 程序時,內建名稱空間都是從 __builtins__ 模塊中加載的,只是 __builtins__ 本身是對 Python 內建模塊 __builtin__ 的引用,而這種引用又分下面兩種情況: 
* 如果是在主模塊 __main__ 中,__builtins__ 直接引用 __builtin__ 模塊,此時模塊名 __builtins__ 與模塊名 __builtin__ 指向的都是同一個模塊,即 內建模塊(這裡要注意變量名和對象本身的區別 
* 如果不是在主模塊中,那麼 __builtins__ 只是引用了 __builtin__.__dict__


2017年9月7日 星期四

[ Git 常見問題 ] Reset or revert a specific file to a specific revision using Git?

Source From Here 
Question 
I have made some changes to a file which has been committed a few times as part of a group of files, but now want to reset/revert the changes on it back to a previous version. I have done a git log along with a git diff to find the revision I need, but just have no idea how to get the file back to its former state in the past. 

How-To 
Assuming the hash of the commit you want is c5f567: 
# git checkout c5f567 -- file1/to/restore file2/to/restore

The git checkout main page gives more information. If you want to revert changes made to the files in commit c5f567 and subsequent commits, you have to pass the commit just before it. You can use refer to that commit as c5f567~1. As a side note, I've always been uncomfortable with this command because it's used for both ordinary things (changing between branches) and unusual destructive things (discarding changes in the working directory). 

Below is a demonstration on above description: 
# vi Test // Add line "This is for revert testing" 
# git add Test 
# git commit -m 'R1' 
[master dbc6fb1] R1 
1 file changed, 2 insertions(+)
 

# vi Test // Add line "This is for revert testing again" 
# git add Test 
# git commit -m 'R2' 
[master 32e38df] R2 
1 file changed, 1 insertion(+)
 

# git log // Check commitment history 
commit 32e38df9489f9c24b6621b59c9109fb7f991e329 
Author: johnklee  
Date: Fri Sep 8 09:09:57 2017 +0800 

R2 

commit dbc6fb157236990cb0450d7355a6faa6c044f2b9 
Author: johnklee  
Date: Fri Sep 8 09:09:36 2017 +0800 

R1 
...
 

# git checkout dbc6fb157236990cb0450d7355a6faa6c044f2b9 -- Test // Revert file Test back to version 'R1' 
# git checkout dbc6fb157236990cb0450d7355a6faa6c044f2b9~1 -- Test // Revert file Test back to the version just before 'R1' 
# git diff 32e38df9489f9c24b6621b59c9109fb7f991e329 -- Test // Check the diff with version 'R2' by file Test 
diff --git a/Test b/Test 
index fcdc1cb..e9f5ce3 100644 
--- a/Test 
+++ b/Test 
@@ -10,6 +10,3 @@ This line is added by branch test (1) 

@ Update from test branch (4) 
@ Update from test branch (5) 
- 
-This is for revert testing 
-This is for revert testing again


[Python 文章收集] Python 的 exec、eval 詳解

Source From Here 
Preface 
Python 動態執行字符串代碼片段(也可以是文件),一般會用到 execeval。 

exec 
The exec statement
exec_stmt: "exec" expression ["in" expression ["," expression]]

注意:exec 是一個語法聲明,不是一個函數.也就是說和 if, for 一樣. 官方文檔對於 exec 的解釋: 
This statement supports dynamic execution of Python code.

exec 的第一個表達式可以是: 
* 代碼字符串
* 文件對象
* 代碼對象
* tuple

前面三種情況差不多,第四種比較特殊最後講. 如果忽略後面的可選表達式, exec 後面代碼將在當前域執行: 
>>> a = 2
>>> exec "a=1"
>>> a
1

如果在表達式之後使用 in 選項指定一個 dict,它將作為 global 和 local 變量作用域: 
>>> a = 10
>>> b = 20
>>> g = {'a':6, 'b':8}
>>> exec "global a; print a,b" in g // g 同時代表 global 與 local 變量作用域
6 8
>>> exec "print a,b" in g
6 8

如果 in 後詳指定兩個表達式,它們將分別用作 global 和 local 變量作用域: 
>>> a = 1
>>> b = 2
>>> c = 3
>>> g = {'a':10, 'b':20}
>>> l = {'a':100, 'b':200, 'c':300}
>>> exec "global a; print a,b,c" in g,l // a 是從 global 作用域
10 200 300
>>> exec "print a,b,c" in g,l // a 是來自 local 作用域
100 200 300

現在說下 tuple 的情況,這也是導致很多人誤以為 exec 是一個函數的原因 (在 3.x 版 exec 已經是函數.)。如果第一個表達式是 tuple
# exec(expr, globals) // 它等效与 exec expr in globals
# exec(expr, globals, locals) // 它等效与 exec expr in globals,locals

eval 
eval 通常用來執行一個字符串表達式,並返回表達式的值。 
eval(expression[, globals[, locals]])

有三個參數,表達式字符串,globals 變量作用域,locals變量作用域。其中第二個和第三個參數是可選的。如果忽略後面兩個參數,則 eval 在當前作用域執行。 
>>> a = 1
>>> eval("a + 1")
2
>>> eval("a = 2")
Traceback (most recent call last):
...

如果指定 globals 參數: 
>>> a = 1
>>> g = {'a':10}
>>> eval("a + 1", g)
11

如果指定 locals 參數: 
>>> a = 10
>>> b = 20
>>> c = 30
>>> g = {'a':1, 'b':2}
>>> l = {'b':200, 'c':300}
>>> eval('a+b+c', g, l) // a 來自 global 作用域; b, c 來自 local 作用域
501

如果要嚴格限制 eval 執行,可以設置 globals 為 __builtins__ (在 Python3+ 為 builtins),這樣這個表達式只可以訪問 __builtin__ module (This module provides direct access to all ‘built-in’ identifiers of Python)。 
>>> globals().keys()
['__builtins__', '__name__', '__doc__', '__package__']
>>> __builtins__

execeval 給我帶來了極大的靈活性,同時也帶來了隱含的危險性,當我們使用它們的時候應該總是記得詳細指定其執行的作用域。 

Supplement 
深入理解 Python 中的 __builtin__ 和 __builtins__ 
What's the deal with __builtins__ vs __builtin__

2017年9月6日 星期三

[ Python 常見問題 ] Python exec 出現 nested function with free variables 錯誤?

Source From Here 
Question 
考慮下面代碼: 
  1. def t(ab=233):  
  2.     def t2():print ab  
  3.     exec 'print ab'  
  4.   
  5. # SyntaxError: unqualified exec is not allowed in function 't'  
  6. # because it contains a nested function with free variables  
註釋掉 t2 就沒問題,t2 裡 print 233 也沒錯,t2 裡面為什麼不能引用變量啊! 就算是語法這麼規定的,為什麼我只是定義了一個函數就要報錯? 

How-To 
問題的解決方案參見:In Python, why doesn't exec work in a function with a subfunction?用戶 Lennart Regebro 的回答)exec 的完整語法參見:6.13 The exec statement. 問題的詳細原因參見:PEP 227 -- Statically Nested Scopes,主要是在 Backwards compatibility 小節的第二個例子。 

說一下我對 PEP 227 的理解。python是一門靜態作用域的語言,PEP 227 在 python 2.1 開始引入了一項語言新特性——靜態嵌套作用域。當然也伴隨而來了一些問題。在函數作用域內執行 bare exec 或 import * 的時候,可能會在 local namespace 中創建新的名稱,但由於是動態語句,python 在編譯的時候無法確定這一點。導致的問題就是,python無法在編譯時確定嵌套函數中的名稱綁定。比如下面 inner 函數中的名稱 x,編譯時無法確定是否應該綁定到 global namespace 的名稱 x,因為 exec str 可能會在 local namespace 中也創建名稱 x: 
  1. x = 1  
  2. def outer(str):  
  3.     exec str          # str = 'x=2'  
  4.     def inner():  
  5.         print x  
這種情況下有兩種策略: 
編譯時放任不管,運行時再根據實際情況綁定: 
* 這會導致人在看代碼的時候,也無法確定inner函數內的名稱x到底如何綁定
* 違背了 python 的靜態作用域設計目標

編譯時直接強行靜態綁定,忽略動態語句可能的影響 
* 如果動態語句真的在當前作用域中創建了新的名稱綁定,那這些名稱就對當前作用域中的嵌套作用域不可見了,這違背了PEP 227本身的設計目標

兩種策略各有弊病,所以PEP 227最終決定: 
SyntaxError: unqualified exec is not allowed in function 'xxxx' because it contains a nested function with free variables

根據這個異常提示信息,也可以反過來總結出拋異常的兩個條件: 
* bare exec(unqualified exec),或者 import *
* 嵌套函數內部引用了自由變量,也就是外部名稱

要解決這個問題,只需使用 exec ... in ... 語句就可以了。這樣,exec 語句就能保證不會對 local namespace 產生任何影響(修改locals()不會真的影響局部變量),編譯器就能放心的忽略 exec 語句的影響而直接靜態綁定了,這也是為什麼 exec 需要是個關鍵字而不是庫函數。其實這種方法跟上面說的第二種策略是一個意思,只不過把控制權從編譯器交給了開發者。python 3 中對 exec 語義做了一些修改,就不存在這個問題了,所以它不需要再是關鍵字,變成了一個函數。具體還沒了解過。 

Supplement 
Python 的 exec、eval 詳解

[ Py DS ] Ch3 - Data Manipulation with Pandas (Part5)

Source From  Here   Pivot Tables   We have seen how the  GroupBy  abstraction lets us explore relationships within a dataset. A pivot ta...