廖雪峯Python教程學習筆記(5)

8. 模塊

在 Python 中,一個 .py 文件就稱之爲一個模塊(Module)。

模塊是一組Python代碼的集合,可以使用其他模塊,也可以被其他模塊所使用。

模塊命名不要和系統模塊名衝突,在 Python 交互式環境中,import abc,若成功說明存在此名稱的系統模塊;否則,不存在。

8.1 使用模塊

sys模塊是 Python 內置的一個模塊。

使用 sys 模塊的第一步,就是導入該模塊:

import sys

導入 sys 模塊後,就有了 sys 變量指向該模塊,這樣通過 sys 變量,就可以訪問 sys 模塊的所有功能。

作用域

正常的函數和變量名是公開的(public),可以被直接引用,比如 abc, PI等;

類似__xxx__ 這樣的變量是特殊變量,可以被直接引用,但是有特殊的用途,比如 __author____name__ 就是特殊變量,自己一般不要這樣命名;

類似_xxx__xxx 這樣的函數或變量就是非公開的(private),不應該被直接引用,如 _abc, __abc 等。

_xxx 這樣的函數或變量是可以被外界訪問到的。但是,按照約定俗成的規定,這樣的函數或變量表示的含義是“雖然我可以被訪問,但是請把我視爲私有變量,不要隨意訪問”。

8.2 安裝第三方模塊

在 Python 中,安裝第三方模塊,是通過包管理工具 pip 完成的。

第三方庫,必須先知道該庫的名稱,可以在官網或者pypi上搜索。
比如安裝 Pillow,命令是:

pip install Pillow

安裝常用模塊

使用Anaconda,這是一個基於Python的數據處理和科學計算平臺,它已經內置了許多非常有用的第三方庫。Anaconda 自帶 Python。

9. 面向對象編程

在 Python 中,所有數據類型都可以視爲對象,也可以自定義對象。

9.1 類和實例

在 Python 中,通過 class 關鍵字來定義類,如 Student 類:

class Student(object):
    pass

class 關鍵字後面跟着的 Student 是類名,類名通常以大寫字母開頭,這點和 Java 是一致的。

緊接着類名的是(object),表示該類是從哪個類繼承下來的。通常,如果沒有合適的繼承類,就用 object類。object類是所有類最終都會繼承的類。

pass 是空語句,一般用作佔位語句,是爲了保證程序結構的完整性。如果不加的話,會拋出異常:IndentationError: expected an indented block。之前的筆記裏提到過。這裏算是複習了。

使用 Student類創建實例:

bart = Student()
print(bart) # <__main__.Student object at 0x7f64b90fc898>
print(Student) # <class '__main__.Student'>

從打印信息可以看出,bart 指向的是一個 Student 的實例,它有內存地址;而Student 本身是一個類。

可以自由地給一個實例綁定屬性

這一點真的很新鮮,它是怎麼做到的?

bart.name = 'Bart Smith'
print(bart.name) # Bart Smith
bart.age = 32
print(bart.age) # 32
bart.salary = 20000.0
print(bart.salary) # 20000.0

把必須綁定的屬性強制寫進去

class Student(object):
    
    def __init__(self, name, score):
        self.name = name
        self.score = score

__init__ 方法的作用:通過一個特殊的 __init__ 方法,在創建實例的時候把 namescore等屬性綁定上去。

__init__是一個特殊的方法,它的第一個參數永遠是 self,表示創建的實例本身。

__init__ 方法內部,把各種屬性綁定到 self

使用 Student 類創建實例:

# bart = Student() # 報錯:TypeError: __init__() missing 2 required positional arguments: 'name' and 'score'
bart = Student('Bart Smith', 0)
print(bart) # <__main__.Student object at 0x7fad2fbf2b00>
print('name = %s, score = %s' % (bart.name, bart.score)) # name = Bart Smith, score = 0

注意到現在使用 Student() 這種方式直接報錯,這是因爲我們在類中使用了 __init__ 方法,就不能再傳入空參數了,必須傳入與 __init__相匹配的參數,但 self 是個例外,我們不需要傳 self,Python 解釋器會自己把實例變量 self 傳進去。因此,需要傳入的是 namescore

普通函數和類中定義的函數有什麼區別?

區別只有一個,就是在類中定義的函數,它的第一個參數永遠是實例變量 self,而且,在調用時,不用傳遞該參數。

數據封裝

在類中定義方法,通過在實例上調用方法,就可以直接操作對象內部的數據,不需要知道方法內部的實現細節。

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 get_grade(self):
        if self.score >= 90:
            return 'A'
        elif self.score >= 60:
            return 'B'
        else:
            return 'C'

# 使用 Student 類
bart = Student('Peter Wang', 59)
bart.print_score() # 打印結果:Peter Wang: 59
print(bart.get_grade()) # C

特別注意的一點

和靜態語言不同,Python 允許對實例變量綁定任何數據,也就是說,對於兩個實例變量,雖然它們都是同一個類的不同實例,但擁有的變量名稱可能不同。

class Student(object):
    def __init__(self, name, score):
        self.name = name
        self.score = score
    
# 對不同的實例變量綁定不同的數據
wang = Student('zhichao', 100)
li = Student('xiaolong', 100)
wang.age = 18
li.height = 176
print(wang.age) # 打印:18
# print(li.age) # 此行報錯
'''
Traceback (most recent call last):
  File "student4.py", line 12, in <module>
    print(li.age)
AttributeError: 'Student' object has no attribute 'age'
'''
print(li.height) # 打印:176
# print(wang.height) # 此行報錯
'''
Traceback (most recent call last):
  File "student4.py", line 20, in <module>
    print(wang.height)
AttributeError: 'Student' object has no attribute 'height'
'''

9.2 訪問限制

隱藏變量,對外暴露 getter/setter 方法

# 增加對屬性的訪問限制, 暴露出訪問屬性的方法
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))

    # getter/setter 方法
    def get_name(self):
        return self.__name
    
    def set_name(self, name):
        self.__name = name

    def get_score(self):
        return self.__score

    def set_score(self, score):
        if score >= 0 and score <= 100:
            self.__score = score
        else:
            raise ValueError('illegal score')

wang = Student('zhichao', 100)
# print(wang.__name) # 此行報錯
'''
Traceback (most recent call last):
  File "student6_access.py", line 12, in <module>
    print(wang.__name)
AttributeError: 'Student' object has no attribute '__name'
'''
print('%s: %s' % (wang.get_name(), wang.get_score())) # 打印:zhichao: 100
wang.set_name('zhijie')
# wang.set_score(150) # 不合法的分數,拋異常
wang.set_score(99)
print('%s: %d' % (wang.get_name(), wang.get_score())) # 打印:zhijie: 99

9.3 繼承和多態

繼承

class Animal(object):
    def run(self):
        print('Animal is running...')

class Dog(Animal):
    pass
class Cat(Animal):
    pass

dog = Dog()
dog.run() # 打印:Animal is running...

cat = Cat()
cat.run() # 打印: Animal is running...

上面的代碼裏,Dog 類 繼承了 Animal 類,寫法是把 Animal 寫在 Dog 後面的圓括號裏面。這時,我們說 Dog 類是 Animal 類的子類,Animal 類是 Dog 類的基類、父類或超類。

通過繼承,子類獲取了父類的全部功能。

子類還可以增加一些方法。

多態

# 多態
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...')

def run_twice(animal):
    animal.run()
    animal.run()


run_twice(Animal())
run_twice(Dog())
run_twice(Cat())

'''
Animal is running...
Animal is running...
Dog is running...
Dog is running...
Cat is running...
Cat is running...
'''

多態的含義:父類引用指向子類對象。

多態的作用:調用方只管調用,不管細節,當我們新增一種 Animal 的類型時,只要保證 run() 方法編寫正確,不用管原來的代碼是如何調用的。這符合“開閉”原則(Open Closed Principle,OCP):

對擴展開放(open for extension):允許新增 Animal 類的子類;
對修改關閉( closed for modification):新增了 Animal 類的子類,不需要修改依賴 Animal 類的 run_twice() 等函數。

特別注意的一點

對於靜態語言(如 Java)來說,傳入run_twice() 方法中的類型,需要是 Animal 類型,或者是 Animal 類型的子類型,否則,無法調用 run_twice() 方法。
而 Python 是一門動態語言,則不一定需要傳入的是 Animal 類型或者其子類型,只要保證傳入的對象有一個 run() 方法就可以了。

例如,這裏我們定義一個火車Train 類,它有一個 run() 方法,它就可以傳入 run_twice() 方法。

class Train(object):

    def run(self):
        print('Train is running...')

run_twice(Train())
'''
Train is running...
Train is running...
'''

這就是動態語言的“鴨子類型”,它並不要求嚴格的繼承體系,一個對象只要“看起來像鴨子,走起路來像鴨子”,那它就可以被看做是鴨子。

9.4 獲取對象信息

這裏主要學習的是 type() 函數和 isinstance() 函數的使用。它們都是 Python 的內置函數。

type() 函數

type() 函數用來獲取對象的類型,返回的是對應的 Class 類型。

它可以用於基本類型數據:

#  使用 type()來判斷基本類型
print(type(123)) # <class 'int'>
print(type('hello')) # <class 'str'>
print(type(None))  # <class 'NoneType'>
print(type(3.1415926)) # <class 'float'>
print(type(True)) # <class 'bool'>

可以判斷指向函數或類的變量的類型:

print(type(abs)) # <class 'builtin_function_or_method'>
class Animal(object):
    pass
a = Animal()
print(type(a)) # <class '__main__.Animal'>
b = Animal
print(type(b)) # <class 'type'>

判斷 type() 函數作用的變量的類型是否相等:

print(type(123) == type(456)) # True
print(type(123) == int) # True
print(type('abc') == type('123')) # True
print(type('abc') == str) # True
print(type(6.18) == type(3.14)) # True
print(type(6.18) == float) # True
print(type(1 > 0) == type(2 > 1)) # True
print(type(1 > 0) == bool) # True

可以看到,可以讓基本數據類型的 intfloat 等參與比較。

判斷 type() 函數作用的對象是否是函數:

import types # 引入 types 模塊,這裏需要它裏面定義的一些常量
def fn():
    pass

print(type(fn) == types.FunctionType) # True
print(type(abs) == types.BuiltinMethodType) # True
print(type(lambda x:  x + 1) == types.LambdaType) # True
print(type((x for x in range(10))) == types.GeneratorType) # 這種是生成器列型 打印 :True 

isinstance() 函數

class Fruit(object):
    pass

class Apple(Fruit):
    pass

class Hongfushi(Apple):
    pass

f = Fruit()
a = Apple()
h = Hongfushi()

print(isinstance(f, Fruit)) # True
print(isinstance(a, Fruit)) # True
print(isinstance(h, Fruit)) # True
print(isinstance(a, Hongfushi)) # False

# isinstance 用於基本數據類型
print(isinstance(123, int)) # True
print(isinstance(True, bool)) # True
print(isinstance(3.14, float)) # True
print(isinstance('hello', str)) # True

# isinstance 判斷一個變量是否某些類型中的一種
print(isinstance([1, 2, 3], (list, tuple))) # True

print('=' * 20, 'I am a divider', '=' * 20)

知識點:type() 函數和 isinstance() 函數的區別是什麼?

type() 不會認爲子類是一種父類類型,不考慮繼承關係。

isinstance() 會認爲子類是一種父類類型,考慮繼承關係。

如果要判斷兩個類型是否相同推薦使用 isinstance()。

dir() 函數

獲取一個對象的所有屬性和方法,使用 dir() 函數,這也是 Python 中的一個內置函數。

print(dir('ABC'))

打印結果:

['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__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']

可以看到返回的是一個 list,裏面包含了str 對象的所有屬性和方法。

# 獲取一個對象的長度, 使用 len() 函數,內部就是調用對象的 __len__ 函數
print(len('ABC'))
print('ABC'.__len__())

在自己的類中使用 __len__ 函數:

# 自己編寫一個類,定義一個 __len__ 方法,也可以使用 len() 函數獲取長度
class MyObject(object):
    def __len__(self):
        return 100

print(MyObject().__len__()) # 100
print(len(MyObject())) # 100

配合 getattr(), setattr() 和 hasattr(), 直接操作一個對象的狀態

# 配合 getattr(), setattr() 和 hasattr(), 直接操作一個對象的狀態
# 這三個都是內置函數
class MyClass(object):
    def __init__(self):
        self.x = 9
    def power(self):
        return self.x * self.x
obj = MyClass()
# 判斷有無屬性 x?
print(hasattr(obj, 'x')) # True
print(obj.x) # 9
# 判斷有無屬性 y? 
print(hasattr(obj, 'y')) # False
# 設置一個屬性 y?
setattr(obj, 'y', 19)
# 再判斷有無屬性 y? 
print(hasattr(obj, 'y')) # True
# 獲取屬性 y
print(getattr(obj, 'y')) # 19
# 獲取一個不存在的屬性,拋出異常
# getattr(obj, 'z') # 此行拋出異常,如下所示
'''
  File "attrs.py", line 36, in <module>
    getattr(obj, 'z')
AttributeError: 'MyClass' object has no attribute 'z'
'''
# 獲取屬性,如果不存在,返回默認值
print(getattr(obj, 'z', -1)) # -1

# 獲取對象的方法
print(hasattr(obj, 'power')) # True
print(getattr(obj, 'power')) # <bound method MyClass.power of <__main__.MyClass object at 0x7f5d8dd5cf98>>
fn = getattr(obj, 'power')
print(fn) # <bound method MyClass.power of <__main__.MyClass object at 0x7f5d8dd5cf98>>
print(fn()) # 81

9.5 實例屬性和類屬性

# 實例屬性和類屬性
class Student(object):
    # name 是類屬性,歸 Student 類所有
    name = 'Student'

wang = Student()
print(wang.name) # Student

li = Student()
print(li.name) # Student

可以看到,對於 name,我們沒有通過 __init__ 函數進行綁定,也沒有通過對象綁定,但是每個實例都已經有了這個屬性。

類屬性作爲計數器的例子:

# 爲了統計學生人數,可以給Student類增加一個類屬性,每創建一個實例,該屬性自動增加:
class Student(object):
    count = 0

    def __init__(self, name):
        self.name = name
        Student.count = Student.count + 1

需要注意的是,不要對實例屬性和類屬性使用相同的名字,因爲相同名字的實例屬性會屏蔽掉類屬性。

實例屬性和類屬性的區別:

實例屬性屬於各個實例,互不干擾;

類屬性屬於類所有,所有實例共享一個屬性。

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