2.python:從print說到自省
print是一個函式
為什麼print是一個函式呢?可以在互動式直譯器下
輸入:
>>>type(print)
輸出:
<class 'builtin_function_or_method'>
這句話的意思是說,print的型別是一個內建的函式或方法,class只是起指示作用,與類定義時的class作用不同,而'builtin_function_or_method'是一段描述性文字,不需要在意其中的下劃線,我相信它僅僅是python想輸出的一段文字而已,有無這下劃線並不妨礙說明print是一個函式。
如何使用print函式
可以在互動式直譯器下
輸入:
>>>print.__doc__
輸出:
"print(value, ..., sep=' ', end='\\n', file=sys.stdout, flush=False)\n\nPrints the values to a stream, or to sys.stdout by default.\nOptional keyword arguments:\nfile: a file-like object (stream); defaults to the current sys.stdout.\nsep: string inserted between values, default a space.\nend: string appended after the last value, default a newline.\nflush: whether to forcibly flush the stream."
從這段文件來看,要使用print函式,就應該如同文件所給出的那種形式:print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False),可以看到其中非常可能變化的引數是"value,...,",而不需要變化的引數是sep和它後面的引數(預設引數)。如果想要改變列印的方式,就需要改變這些預設引數。程式碼如下:
1 .value, ...,
>>> print('a') # 列印單個字串,其他引數預設 a >>> print('a', 'b', 'c') # 列印多個字串 a b c
2 .sep
>>> print('a', 'b', 'c', sep=',') # 用逗號將結果隔開
a,b,c
3 .end
新建.py檔案,儲存執行
輸入:
print('hello,') # 列印結束預設換行
print('world!')
輸出:
hello,
world!
新建.py檔案,儲存執行
輸入:
print('hell,', end='***') # 列印結束不換行而是加三顆星*
print('world!')
輸出:
hell,***world!
4 .file
輸入
with open(r'D:\python36\print_file.txt', 'w') as f:
print('a', 'b', 'c', file=f)
如果不改變file引數預設值,print直接是顯示在直譯器上,因為sys.stdout指向控制檯,所以sys.stdout是print預設的輸出格式(sys本文不扯),如果改變了file引數,如上面程式碼那樣這時螢幕上就不會再顯示a b c,而是寫在剛剛新建的(如果原先沒有這個檔案就會新建這個檔案)檔案中。
5 .flush=False
從flush引數的值可以看出,flush=False/True,表示是否立刻將輸出語句輸入到引數file指向的物件中(預設是sys.stdout)
輸入
>>> f=open(r'print_flush.txt', 'w')
>>> print('a', 'b', 'c', file=f)
# 此時,txt檔案沒有任何東西寫入,print的內容存在記憶體中,直到檔案關閉,輸出到txt中輸入
>>> f=open(r'print_flush.txt', 'w')
>>> print('a', 'b', 'c', file=f, flush=True) # 此時,txt檔案已經寫入a b c
先不管為何先存到記憶體中,以後再寫。更詳細瞭解可以參考python中文文件
6 .print函式返回了什麼?
既然是一個函式,無論如何,返回值是最重要的,最終還是要看結果嘛,預設情況下,print函式將值(即values)列印到流或sys.stdout,。
print的屬性
python中要檢視一個物件的屬性,可以用dir()函式,
輸入:
dir(print)
輸出:
['__call__', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '
__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__',
'__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__',
'__self__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__text_signature__']
dir函式返回傳遞給它的任何物件的屬性名稱經過排序的列表,這裡的屬性很豐富,注意到這些屬性都是以雙下劃線開始和結束,在python中這有特殊的意義它代表一些特殊的方法,方法也是屬性,這裡主要說下__str__和__repr__
1 .__str__
既然__str__是print的一個屬性,那麼我們就可以通過他的"物件"+."屬性"的形式去檢視這個屬性到底是什麼東西__doc__屬性檢視這個物件的註釋,
輸入:
>>>print.__str__
輸出:
<method-wrapper '__str__' of builtin_function_or_method object at 0x005A0120>
順手牽羊輸入>>>print.__str__,結果也是如同上面那樣,這說明了__str__和__repr__都是print的一個封裝方法,它們所在的記憶體地址是一樣的!
接著輸入:
>>> print.__str__.__doc__
輸出:
'Return str(self).'
str()是什麼?是python的一個內建型別!
繼續輸入:
>>> type(str())
<class 'str'> #輸出
>>> type(str)
<class 'type'> # 輸出
沒錯,按照python文件,str()是一個內建型別,其形式如下:
class str(object='')
class str(object=b'', encoding='utf-8', errors='strict')
那麼str()返回的是什麼?——返回object的字串版本。如下:
輸入:
>>> str('a')
'a' #輸出
>>> str(1)
'1'
2.__repr__
同理所得:
>>> print.__repr__
<method-wrapper '__repr__' of builtin_function_or_method object at 0x005A0120>
>>> print.__repr__.__doc__
'Return repr(self).'
>>> repr('a')
"'a'"
>>> repr(1)
'1'
repr()返回了什麼?repr(object)返回一個包含物件可打印表示的字串。對於許多型別,此函式嘗試返回一個字串,該字串在傳遞給eval()時會產生一個具有相同值的物件,否則該表示是一個用尖括號括起來的字串,其中包含名稱該物件的型別以及經常包括該物件的名稱和地址的附加資訊。一個類可以通過定義一個__repr__()方法來控制該函式為其例項返回的內容。
引號去了哪兒?
>>> print('hello, world!')
hello, world!
無論是在互動式直譯器執行,還是在程式中執行,print('hello, world!')都沒有出現引號,那麼引號去了哪裡?
實際上,前面已經討論過print函式的返回結果是什麼,print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)返回的結果就是values,即((value, ...),那麼問題就剩下這value到底包不包含引號呢?答案是否定的,引號只是標明瞭這個value是一個字串,並不是字串的一部分!可以很容易驗證,比如len('abc')的結果是3而不是5
>>> len('abc')
3
為何引號又出現了?
>>> a='b'
>>> a
'b'
>>> print(a)
b
print函式直接將a打印出來,它是不包含引號的,據說print語句使用str()函式顯示物件,互動直譯器呼叫repr()函式來顯示物件,但我無法通過python自省等方式得知。
最後的最後
最後的最後,能安利一波最好了!沒錯,我就是要安利python的自省機制,這裡給出安利的連結python自省指南,為啥不在開頭給出?因為無論如何,至少到最後能給大家安排上車,就算讀到最後還是不知道筆者在扯啥!