從 Python到Tensorflow 學習之路(三)
阿新 • • 發佈:2018-12-11
從 Python到Tensorflow 學習之路(四)
最近畢業設計題目是研究對抗樣本,要用tensorflow來搭建神經網路,因此python必不可少,這個不是一個傳統的Python學習教程只是把學習Python過程中遇到的問題和經驗記錄下來(基於Python3.5),如果想要一步一步學習Python建議看下面的網站。
Python學習教程
模組
- 在Python中,一個.py檔案就稱之為一個模組(module),其大大提高了程式碼的可維護性。
- 使用模組可以避免函式名和變數名衝突,相同名字的函式和變數完全可以分別存在於不同的模組中。
- 為了避免模組名衝突,Python引入了按照目錄來組織模組的方法,稱為包(package)。
假設我們有兩個模組abc和xyz,與其他模組衝突,我們可以使用包來組織模組,方法是選擇一個頂層包名,比如mycompany
按照如下目錄存放
那麼abc.py模組名就變成了mycompany.abc,類似的xyz.py模組名就變成了mycompany.xyz
注意每一個包目錄下都會有一個__init__.py
,這個檔案是必須存在的。否則Python就把這個目錄當成普通目錄,而不是一個包。__init__.py
可以是空檔案,也可以有Python程式碼。因為__init__.py
本身就是一個模組,它的模組名就是mycompany
使用模組
"""a test module"""
__author__ = 'LWP'
import sys
def test():
args = sys.argv
if len(args)==1:
print('%s!' %args[0])
elif len(args)==2:
print('Hello, %s!' %args[1])
else:
print('Too many arguments!')
if __name__=='__main__' :
test()
- 使用
sys
模組第一步就是匯入該模組:import sys
匯入後,就有了變數sys
指向該模組,利用sys
這個變數,就可以訪問sys
模組的所有功能。sys
模組有一個argv
變數,用list儲存了命令列的所有引數。argv
至少有一個引數,因為第一個引數永遠是該.py
檔案的名稱,當我們執行Python3 blog_test.py
獲得的sys.argv
就是[/home/lwp/PycharmProjects/blog_test/blog_test.py!]
- 注意這個程式碼:
if __name__ = '__main__':
test()
- 當我們在命令列執行
blog_test
模組時,Python直譯器會把一個特殊變數__name__
置為__main__
,而如果在其他地方匯入該blog_test
模組時,if
判斷將失敗,因此這種if
測試可以讓一個模組通過命令列執行時執行一些額外的程式碼,最常見的就是執行測試。- Python直譯器在讀取一個原始檔時,會執行從中找到的所有程式碼。在執行所有程式碼前,它會定義一些特殊的變數。例如,當Python直譯器在執行原始檔模組並把它當做主程式,它會將
__name__
設定為'__main__'
。如果這個原始檔被其他模組匯入那麼__name__
設定為模組本身的名字。因此你可以將一些不希望被匯入的人執行的程式放進檢查中,其他人便不會執行
作用域
在一個模組中,我們可能會定義很多的函式和變數,但有的函式和變數我們希望給別人使用,有的函式我們希望僅僅在模組內部使用。在Python中,是通過字首_
實現的
- 正常的函式和變數名是公開的(public),可以被直接引用,比如
abc, x123
- 類似與
__xx__
這樣的前後雙下劃線變數是特殊變數,可以直接被引用,但是有特殊用途,比如上面的__author__, __name__
- 類似於
_xx, __xx
這樣的前單下劃線或單雙下劃線函式和變數”不應該“被直接引用,而不是”不能“被直接引用,因為Python並沒有一種方法可以完全限制訪問private函式或者變數。
def _private_1(name):
return 'Hello, %s' % name
def _private_2(name):
return 'Hi, %s' % name
def greeting(name):
if len(name) > 3:
return _private_1(name)
else:
return _private_2(name)
import blog_test
print(blog_test.greeting('world'))
# output: Hello, world
面向物件程式設計
一個簡單的處理學生成績的類
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
def print_score(self):
print('%s: %s' % (self.name, self.score))
alice = Student('Alice', 99)
bob = Student('Bob', 85)
alice.print_score()
bob.print_score()
# output:Alice: 99
# Bob:85
類和例項
- class後面緊接著是類名,即Student,類名通常是大寫開頭的單詞,緊接著是
(Object)
,表示該類是從哪個類繼承下來的。- 由於類可以起到模板的作用,因此可以在建立例項的時候,把一些我們認為必須繫結的屬性強制填寫進去。通過定義一個特殊的
__init__
方法,在建立例項的時候,就把name, score
等屬性繫結上去。注意__init__
前後都是雙下劃線。__init__
方法的第一個引數永遠是self
,表示建立的例項本身,因此,在__init__
方法內部,就可以把各種屬性繫結到self
,因為self
就指向建立的例項本身。- 有了
__init__
方法,在建立例項時,不能夠傳入空的引數,必須傳入與__init__
方法相匹配的引數,但是self
不用傳,Python直譯器自己會把例項變數傳進去。
訪問限制
- 從前面Student定義來看,外部的程式碼仍然可以自由地修改一個例項的
name、score
屬性
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
def print_score(self):
print('%s: %s' % (self.name, self.score))
alice = Student('Alice', 99)
bob = Student('Bob', 85)
print(alice.score)
# output:99
- 如果要讓內部屬性不被外部訪問,可以把屬性前面的名稱加上兩個下劃線
__
,在Python中,例項變數名如果以__
開頭,就變成了一個私有變數(private),只有內部可以訪問,外部不能訪問。
class Student(object):
def __init__(self, name, score):
self.__name = name
self.__score = score
def print_score(self):
print('%s: %s' % (self.__name, self.__score))
alice = Student('Alice', 99)
bob = Student('Bob', 85)
# print(alice.__score)
'''Traceback (most recent call last):
File "/home/lwp/PycharmProjects/blog_test/blog_test.py", line 17, in <module>
print(alice.score)
AttributeError: 'Student' object has no attribute '__score'''
alice.print_score()
# output:Alice: 99
- 如果外部程式碼需要獲取name和score怎麼辦,可以增加變數的get方法;如果要允許外部程式碼修改score怎麼辦,可以增加變數的set方法。
class Student(object):
def __init__(self, name, score):
self.__name = name
self.__score = score
def print_score(self):
print('%s: %s' % (self.__name, self.__score))
def set_name(self, name):
self.__name = name
def set_score(self, score):
if 0 <= score <= 100:
self.__score = score
else:
raise ValueError('Bad Score!')
def get_name(self):
return self.__name
def get_score(self):
return self.__score
alice = Student('Alice', 99)
print(alice.get_name())
# output:Alice
print(alice.get_score())
# output:99
alice.set_name('ali')
print(alice.get_name())
# output: ali
alice.set_score(60)
print(alice.get_score())
# output: 60
#alice.set_score(111)
- 在設定引數方法中我們可以對引數進行型別檢查,避免傳入無效引數。
- 在Python中,變數名類似於
__xxx__
的,即雙下劃線開頭,雙下劃線結尾的是特殊變數,特殊變數可以直接訪問,不是private變數,所以不能起__name__, __score__
。- 以一個下劃線開頭的變數,比如
_name
,這樣的例項變數外部是可以訪問的,然而其約定“雖然我可以被訪問,但是請把我視為私有變數,不要隨意訪問”- 雙下劃線開頭的例項變數也並不是完全不能被訪問。不能直接訪問
__name
是因為Python直譯器對外把__name
變數改成了_Student__name
,我們仍然可以通過_Student__name
來訪問__name
變數。但是儘量不要這麼幹!不同版本的Python直譯器可能會把__name
改成不同的變數名
...
print(alice._Student__name)
# output:Alice
- 注意一種錯誤寫法,這個
__name
並不是真的是class內部的__name
,內部的__name
變數已經被Python直譯器自動改成了_Student__name
,而外部程式碼給alice
新增加了一個__name
變數
alice = Student('Alice', 99)
print(alice.get_name())
# output:Alice
alice.__name = 'Bob'
print(alice.__name)
# output:Bob
print(alice.get_name())
# output:Alice
繼承和多型
class Animal(object):
def run(self):
print('Animal is running......')
class Dog(Animal):
def run(self):
print('Dog is running......')
class Cat(Animal):
def run(self):
print('Cat is running......')
a = Animal()
a.run()
# output:Animal is running.....
c = Cat()
c.run()
# output:Cat is running......
d = Dog()
d.run()
# output:Dog is running.....
- 在繼承關係中,如果一個例項的資料型別是某個子類,那麼它的資料型別也可以被看作是父類。但是,反過來就不行
- 對於一個變數,我們只需要知道它是
Animal
型別,無需確切地知道它的子型別,就可以放心地呼叫run()
方法,具體呼叫的run()
方法是作用在哪個物件時,由執行時該物件的確切型別決定,這就是多型。- 呼叫方只管呼叫,不管細節,當我們新增一種
Animal
的子類時,只要確保run()
方法編寫正確,不用管原來的程式碼是如何呼叫的,這即是著名的開閉原則
- 對擴充套件開放:允許新增
Animal
子類- 對修改封閉:不需要修改依賴
Animal
型別的run_twice()
函式
......
def run_twice(animal):
animal.run()
animal.run()
a = Animal()
run_twice(a)
# output:Animal is running......
# output:Animal is running......
c = Cat()
run_twice(c)
# output:Cat is running......
# output:Cat is running......
- 靜態語言vs動態語言:
- 靜態語言(比如Java),如果需要傳入
Animal
型別,則傳入的物件必須是Animal
型別或者它的子類,否則將無法呼叫run()
方法。- 對於Python這種動態語言,則不一定需要傳入Animal型別。我們只需要保證傳入的物件有一個
run()
方法即可。這就是動態語言的“鴨子型別。”它並不要求嚴格的繼承體系,一個物件只要“看起來像鴨子,走起路來像鴨子”,那它就可以被看做是鴨子。
......
class Timer(object):
def run(self):
print('Start.....')
......
run_twice(Timer())
# output:Start.....
# output:Start.....
獲取物件資訊
如何在拿到一個物件的引用時,知道這個物件是什麼型別、有哪些方法?
- 可以判斷物件的基本型別
- 如果一個變數指向函式或者類,也可以用type()判斷
print(type(123))
# output:<class 'int'>
print(type('str'))
# output:<class 'str'>
print(type(None))
# output:<class 'NoneType'>
print(type(abs))
# output:<class 'builtin_function_or_method'>
- 判斷一個物件是否是函式
import types
def fn():
pass
print(type(fn) == types.BuiltinFunctionType)
print(type(abs) == types.BuiltinFunctionType)
isinstance()
可以判斷一個物件是否是該型別本身,或者位於該型別的父繼承鏈上。- 判斷一個變數是否是某些型別的一種
print(isinstance([1, 2, 3], (list, tuple)))
# output: True
print(isinstance({'name': 'Peter', 'age': 20}, (list, tuple)))
# output: False
- 使用
dir()
可以獲得一個物件的所有屬性和方法,它返回一個包含字串的list
print(dir('ABC'))
# output: ['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
- 裡面有
__xx__
的特殊方法以及upper()
等普通屬性或方法- 使用
getattr()、setattr()、hasattr()
直接操作一個物件狀態, 如果試圖獲取不存在的屬性,會丟擲AttributeError的錯誤。可以傳入一個default引數,如果屬性不存在,就返回預設值
class MyObject(object):
def __init__(self):
self.x = 9
def power(self):
return self.x * self.x
obj = MyObject()
print(hasattr(obj, 'power'))
# output: True
print(getattr(obj, 'x'))
# output: 9例項屬性和類屬性使用相同的名字,因為相同名稱的例項屬性將遮蔽掉類屬性,但是當你刪除例項屬性後,再使用相同的名稱,訪問到的將是類屬性。
print(getattr(obj, 'y', 44))
# output: 44
例項屬性和類屬性
由於Python是動態語言,根據類建立的例項可以任意繫結屬性。
- 給例項繫結屬性的方法是通過例項變數,或者通過
self
變數
class Student(object):
def __init__(self, name):
self.name = name
s = Student('Bob')
s.score = 90
- 可以直接在class中定義屬性,這個屬性是類屬性,歸
Student
類所有
class Student(object):
name = 'Student'
s = Student()
print(s.name)
# output: Student(因為例項沒有name屬性,所以會查詢class的name屬性)
print(Student.name)
# output: Student(列印類的name屬性)
s.name = 'Bob'
print(s.name)
# output: Bob(例項屬性的優先順序高於類的屬性)
print(Student.name)
# output: Student
del s.name
print(s.name)
# output: Student
- 千萬不要對例項屬性和類屬性使用相同的名字,因為相同名稱的例項屬性將遮蔽掉類屬性,但是當你刪除例項屬性後,再使用相同的名稱,訪問到的將是類屬性
使用slots限制定義的屬性僅僅對當前類例項起作用
- 之前我們可以給例項繫結一個屬性,其實還可以繫結一個方法。但是給一個例項繫結的方法,對另一個例項是不起作用的。為了給所有例項都繫結方法,可以給class繫結方法
class Student(object):
pass
s = Student()
def set_age(self, age):
self.age = age
from types import MethodType
s.set_age = MethodType(set_age, s)
s.set_age(25)
print(s.age)
# output: 25
class Student(object):
pass
s = Student()
def set_age(self, age):
self.age = age
Student.set_age = set_age
s.set_age(11)
print(s.age)
# output: 11
s2 = Student()
s2.set_age(22)
print(s2.age)
# output: 22
- 如果我們想限制例項的屬性怎麼辦,我們應該在定義class的時候,定義一個特殊的變數
__slot__
變數,來限制該class例項能新增的屬性
class Student(object):
__slots__ = {'name', 'age'}
s = Student()
s.age = 55
s.name = 'Bob'
print(s.age, s.name)
# output: 55 Bob
s.score = 100
# AttributeError: 'Student' object has no attribute 'score'
- 使用score, 其定義的屬性僅僅對當前類例項起作用,對繼承的子類不起作用
使用@property既可以檢查引數又可以用類似於屬性這樣簡單的方式來訪問類變數
- 把一個getter方法變成屬性,只需要加上
@property
即可,此時@property
本身又建立了另外一個裝飾器@score.setter
,負責把一個setter方法變成屬性賦值
class Student(object):
@property
def score(self):
return self._score
@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0~100!')
self._score = value
s = Student()
s.score = 60
print(s.score)
# output: 60
s.score = 9999
# ValueError: score must between 0~100!
- 還可以定義只讀屬性,只定義getter方法,不定義setter方法就是一個只讀屬性
class Student(object):
@property
def birth(self):
return self._birth
@birth.setter
def birth(self, value):
self._birth = value
@property
def age(self):
return 2017-self._birth
s = Student()
s.birth = 1996
print(s.age)
# output : 21
s.age = 30
# AttributeError: can't set attribute
Python中多重繼承
- 在設計類的繼承關係時,通常主線都是單一繼承下來的,如果需要混入額外的功能,通常是通過多重繼承實現,這種設計稱為MixIn
Python中定製類
- Python中列印一個類例項需要自定義
__str__()
方法, 但是這樣仍需要print,直接顯示變數呼叫的不是__str__()
而是__repr__()
,兩者的區別在於__str()__
返回的是使用者看到的字串,而__repr()__
返回的是程式開發者看到的字串。
class Student(object):
def __init__(self, name):
self.name = name
def __str__(self):
return 'Student object (name: %s)' % self.name
__repr__ = __str__
print(Student('Bob'))
# output: Student object (name: Bob)
s = Student('Bob')
- 如果一個類想被用於
for...in..
那麼必須要實現__iter__()
方法,該方法返回一個迭代物件,Python的for迴圈會不斷呼叫該迭代物件的__next__()
方法拿到迴圈的下一個值,知道遇到StopIteration
錯誤時退出迴圈。
下面是一個斐波那契數列
class Fib(object):
def __init__(self):
self.a, self.b = 0, 1
def __iter__(self):
return self
def __next__(self):
self.a, self.b = self.b, self.a+self.b
if self.a > 10000:
raise StopIteration()
return self.a
for n in Fib():
print(n)
- Fib例項雖然能夠作用於for迴圈,但是不能像list一樣支援索引,我們需要實現
__getitem__()
方法,但是這個實現沒有對slice中的step引數作處理,也沒有對負數進行處理
class Fib(object):
def __init__(self):
self.a, self.b = 0, 1
def __iter__(self):
return self
def __next__(self):
self.a, self.b = self.b, self.a+self.b
if self.a > 10000:
raise StopIteration()
return self.a
def __getitem__(self, item):
if isinstance(item, int):
a, b = 1, 1
for x in range(item):
a, b = b, a+b
return a
if isinstance(item, slice):
start = item.start
stop = item.stop
if start is None:
start = 0
a, b = 1, 1
L = []
for x in range(stop):
if x >= start:
L.append(a)
a, b = b, a+b
return L
f = Fib()
print(f[0])
# output: 1
print(f[10])
# output: 89
print(f[1:10])
# output:[1, 2, 3, 5, 8, 13, 21, 34, 55]
- 正常情況下,我們呼叫類中方法或者屬性時,如果不存在就會報錯,但是我們可以通過
__getattr__()
方法動態返回一個屬性
class Student(object):
def __init__(self, name):
self.name = name
def __getattr__(self, attr):
if attr == 'age':
return lambda: 25
raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr)
s = Student('Bob')
print(s.age())
# output: 25
print(s.score())
# AttributeError: 'Student' object has no attribute 'score'
- 我們可以利用
__call__()
方法,直接對例項進行呼叫, 所以可以把物件看成函式,把函式看成物件。我們可以通過callable
函式來判斷一個物件是否能被呼叫
class Student(object):
def __init__(self, name):
self.name = name
def __call__(self):
return ('Student name is %s'% self.name)
s = Student('Bob')
print(s())
- 使用列舉類,可以從
Enum
派生出自定義類
from enum import Enum, unique
@unique
class Weekday(Enum):
Sun = 0
Mon = 1
Tue = 2
Wed = 3
Thu = 4
Fri = 5
Sat = 6
day1 = Weekday.Mon
print(day1)
print(Weekday['Tue'])
print(Weekday(1))
print(Weekday.Thu.value)
for name, member in Weekday.__members__.items():
print(name, ' ==> ', member)
"""
Weekday.Mon
Weekday.Tue
Weekday.Mon
4
Sun ==> Weekday.Sun
Mon ==> Weekday.Mon
Tue ==> Weekday.Tue
Wed ==> Weekday.Wed
Thu ==> Weekday.Thu
Fri ==> Weekday.Fri
Sat ==> Weekday.Sat
"""