從 Python到Tensorflow 學習之路(三)

從 Python到Tensorflow 學習之路(四)


最近畢業設計題目是研究對抗樣本,要用tensorflow來搭建神經網絡,因此python必不可少,這個不是一個傳統的Python學習教程只是把學習Python過程中遇到的問題和經驗記錄下來(基於Python3.5),如果想要一步一步學習Python建議看下面的網站。
Python學習教程


模塊

  • 在Python中,一個.py文件就稱之爲一個模塊(module),其大大提高了代碼的可維護性。
  • 使用模塊可以避免函數名和變量名衝突,相同名字的函數和變量完全可以分別存在於不同的模塊中。
  • 爲了避免模塊名衝突,Python引入了按照目錄來組織模塊的方法,稱爲包(package)。
    假設我們有兩個模塊abc和xyz,與其他模塊衝突,我們可以使用包來組織模塊,方法是選擇一個頂層包名,比如mycompany按照如下目錄存放
    snapshot2.png-2.8kB
    那麼abc.py模塊名就變成了mycompany.abc,類似的xyz.py模塊名就變成了mycompany.xyz

注意每一個包目錄下都會有一個__init__.py,這個文件是必須存在的。否則Python就把這個目錄當成普通目錄,而不是一個包。__init__.py可以是空文件,也可以有Python代碼。因爲__init__.py本身就是一個模塊,它的模塊名就是mycompany

使用模塊
snapshot4.png-31.1kB

"""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
"""

我們下期見~


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章