1. 初識(shí)面向?qū)ο?/h1>
1.1 介紹
- 步驟介紹
面向?qū)ο蟮母攀?br>
面向?qū)ο蟮膶?shí)現(xiàn)
面向?qū)ο蟮膽?yīng)用
內(nèi)存管理
進(jìn)程、線程、協(xié)程
- 概要
面向?qū)ο蟮娜筇卣?br>
面向?qū)ο笕筇卣鞯呐e例
- 目標(biāo)
了解面向?qū)ο缶幊痰乃悸?br>
了解面向?qū)ο笈c面向過程的區(qū)別
了解面向?qū)ο缶幊痰膬?yōu)點(diǎn)
了解面向?qū)ο蟮娜筇卣?/li>
1.2 什么是面向?qū)ο?/h2>
- 類與對(duì)象
類是抽象的概念。
對(duì)象是類的實(shí)例。
- 封裝
隱藏實(shí)現(xiàn)細(xì)節(jié),提供訪問接口。
- 繼承
一種類與類之間的派生關(guān)系。
- 多態(tài)
父類引用指向子類對(duì)象。
2. 類的特性
2.1 類的定義與實(shí)現(xiàn)
- 類的方法
dir 列出類的所有屬性及方法
help 查看類、方法的幫助信息
__init__ 構(gòu)造方法
__del__ 析構(gòu)函數(shù)
__doc__ 顯示類的文檔注釋字符串
__module__ 顯示類所在的模塊名
__bases__ 一個(gè)元組顯示類所繼承的所有父類
- 在編寫代碼時(shí)只寫框架思路,具體實(shí)現(xiàn)還未編寫就可以用
pass 進(jìn)行占位,使程序不報(bào)錯(cuò),不會(huì)進(jìn)行任何操作。
x = 5
if x < 6:
pass
- 判斷是否是某個(gè)類的實(shí)例
isinstance(instance, Class)
- 類與對(duì)象
class Cat(object):
""" 貓科動(dòng)物類 """
tag = '貓科動(dòng)物'
def __init__(self, name, age, sex = "公"):
self.name = name
self.__age = age
self.sex = sex
def show_info(self):
info = "我叫{}, 今年{}歲, 性別{}".format(self.name, self.__age, self.sex)
print(info)
def get_age(self):
return self.__age
def set_age(self, age):
self.__age = age
def eat(self):
print("吃魚")
def catch(self):
print("捉老鼠")
class Dog(object):
pass
if __name__ == '__main__':
# 小黑對(duì)象
black_cat = Cat('小黑', 2)
black_cat.eat() # 吃魚
black_cat.show_info() # 我叫小黑, 今年2歲
print(black_cat.name) # 小黑
# print(black_cat.__age) # 靜態(tài)屬性,無法訪問。會(huì)報(bào)錯(cuò)!'AttributeError: 'Cat' object has no attribute '__age'
black_cat.set_age(1)
black_cat.show_info() # 我叫小黑, 今年1歲, 性別公
print(Cat.tag) # 貓科動(dòng)物
print(black_cat.tag) # 貓科動(dòng)物
print(isinstance(black_cat, Cat)) # True
# 小白對(duì)象
white_cat = Cat('小白', 3, '母')
white_cat.show_info() # 我叫小白, 今年3歲, 性別母
white_cat.__age = 4 # 靜態(tài)屬性,無法修改。不會(huì)報(bào)錯(cuò)!
white_cat.show_info() # 我叫小白, 今年3歲, 性別母
print(isinstance(white_cat, Dog)) # False
面向?qū)ο蟮母攀?br> 面向?qū)ο蟮膶?shí)現(xiàn)
面向?qū)ο蟮膽?yīng)用
內(nèi)存管理
進(jìn)程、線程、協(xié)程
面向?qū)ο蟮娜筇卣?br> 面向?qū)ο笕筇卣鞯呐e例
了解面向?qū)ο缶幊痰乃悸?br> 了解面向?qū)ο笈c面向過程的區(qū)別
了解面向?qū)ο缶幊痰膬?yōu)點(diǎn)
了解面向?qū)ο蟮娜筇卣?/li>
- 類與對(duì)象
類是抽象的概念。
對(duì)象是類的實(shí)例。 - 封裝
隱藏實(shí)現(xiàn)細(xì)節(jié),提供訪問接口。 - 繼承
一種類與類之間的派生關(guān)系。 - 多態(tài)
父類引用指向子類對(duì)象。
2. 類的特性
2.1 類的定義與實(shí)現(xiàn)
- 類的方法
dir列出類的所有屬性及方法
help查看類、方法的幫助信息
__init__構(gòu)造方法
__del__析構(gòu)函數(shù)
__doc__顯示類的文檔注釋字符串
__module__顯示類所在的模塊名
__bases__一個(gè)元組顯示類所繼承的所有父類 - 在編寫代碼時(shí)只寫框架思路,具體實(shí)現(xiàn)還未編寫就可以用
pass進(jìn)行占位,使程序不報(bào)錯(cuò),不會(huì)進(jìn)行任何操作。
x = 5
if x < 6:
pass
- 判斷是否是某個(gè)類的實(shí)例
isinstance(instance, Class) - 類與對(duì)象
class Cat(object):
""" 貓科動(dòng)物類 """
tag = '貓科動(dòng)物'
def __init__(self, name, age, sex = "公"):
self.name = name
self.__age = age
self.sex = sex
def show_info(self):
info = "我叫{}, 今年{}歲, 性別{}".format(self.name, self.__age, self.sex)
print(info)
def get_age(self):
return self.__age
def set_age(self, age):
self.__age = age
def eat(self):
print("吃魚")
def catch(self):
print("捉老鼠")
class Dog(object):
pass
if __name__ == '__main__':
# 小黑對(duì)象
black_cat = Cat('小黑', 2)
black_cat.eat() # 吃魚
black_cat.show_info() # 我叫小黑, 今年2歲
print(black_cat.name) # 小黑
# print(black_cat.__age) # 靜態(tài)屬性,無法訪問。會(huì)報(bào)錯(cuò)!'AttributeError: 'Cat' object has no attribute '__age'
black_cat.set_age(1)
black_cat.show_info() # 我叫小黑, 今年1歲, 性別公
print(Cat.tag) # 貓科動(dòng)物
print(black_cat.tag) # 貓科動(dòng)物
print(isinstance(black_cat, Cat)) # True
# 小白對(duì)象
white_cat = Cat('小白', 3, '母')
white_cat.show_info() # 我叫小白, 今年3歲, 性別母
white_cat.__age = 4 # 靜態(tài)屬性,無法修改。不會(huì)報(bào)錯(cuò)!
white_cat.show_info() # 我叫小白, 今年3歲, 性別母
print(isinstance(white_cat, Dog)) # False
注釋:__開頭表示私有變量/方法。靜態(tài)屬性/方法被實(shí)例繼承(與Java相同,與JavaScript不同)。
注意:tag為靜態(tài)屬性,而不是成員屬性。
2.2 類的繼承與多態(tài)
- 方法介紹
(1) 判斷是否是父子類關(guān)系
issubclass(Child, Parent)
(2) 子類調(diào)用父類方法
def eat(self):
super(Cat, self).eat()
print("貓還喜歡吃魚")
- 繼承示例
class Animal(object):
""" 動(dòng)物基類 """
tag = "動(dòng)物"
def __init__(self, name):
self.name = name
def eat(self):
print("動(dòng)物都吃東西")
class Cat(Animal):
""" 貓科動(dòng)物 """
tag = "貓科動(dòng)物"
def __init__(self, name, age):
super(Cat, self).__init__(name)
self.__age = age
def get_age(self):
return self.__age
def eat(self):
super(Cat, self).eat()
print("貓還吃魚")
def catch(self):
print("捉老鼠")
if __name__ == '__main__':
print(issubclass(Cat, Animal)) # True
print(issubclass(Cat, object)) # True
cat = Cat('小黑', 3)
print(cat.name) # 小黑
print('年齡: {}'.format(cat.get_age())) # 年齡: 3
print(cat.tag) # 貓科動(dòng)物
cat.eat() # 動(dòng)物都吃東西 貓還吃魚
- 多重繼承
class NavMixin(object):
"""導(dǎo)航功能"""
def nav(self):
print("具備導(dǎo)航功能")
class Animal(object):
""" 動(dòng)物基類 """
tag = "動(dòng)物"
def __init__(self, name):
self.name = name
def eat(self):
print("動(dòng)物都吃東西")
class Dog(Animal):
"""犬科動(dòng)物"""
tag = '犬科動(dòng)物'
def eat(self):
super(Dog, self).eat()
print("狗喜歡啃骨頭")
class GoldDog(Dog, NavMixin):
"""金毛尋回犬"""
pass
print(issubclass(GoldDog, Dog)) # True
print(issubclass(GoldDog, NavMixin)) # True
gd = GoldDog("小金毛")
gd.eat() # 動(dòng)物都吃東西 # 狗喜歡啃骨頭
gd.nav() # 具備導(dǎo)航功能
注意:如果多重繼承的父類定義了同名方法,則調(diào)用位置靠前的父類的方法。
- 如果輸出內(nèi)容含有花括號(hào)時(shí),需要加一層
{}
print("{{{}}}".format("字符串")) # {字符串}
- 子類調(diào)用父類構(gòu)造函數(shù)
①super().__init__(參數(shù)1, 參數(shù)2)
②super(子類, self).__init__(參數(shù)1, 參數(shù)2)
③父類.__init__(self, 參數(shù)1, 參數(shù)2)
第一種方式僅在python3中支持。
多重繼承時(shí),前兩種方式只能調(diào)第一個(gè)父類的構(gòu)造方法,第三種方式可以調(diào)用任意一個(gè)父類的構(gòu)造方法。
class ParentA(object):
def __init__(self):
print("I am ParentA")
def say(self):
print("Say ParentA")
class ParentB(object):
def __init__(self):
print("I am ParentB")
def say(self):
print("Say ParentB")
class Children(ParentA, ParentB):
def __init__(self):
super(Children, self).__init__()
ParentB.__init__(self)
Children()
# I am ParentA
# I am ParentB
- 圖形面向?qū)ο笫纠?/li>
class Point(object):
def __init__(self, x, y):
self.x = x
self.y = y
def string(self):
print("{{X:{}, Y:{}}}".format(self.x, self.y))
class Circle(Point):
def __init__(self, x, y, radius):
super(Circle, self).__init__(x, y)
self.radius = radius
def string(self):
print("該圖形初始化點(diǎn)為:{{X:{}, Y:{}}}; {{半徑為:{}}}".format(self.x, self.y, self.radius))
class Size(object):
def __init__(self, width, height):
self.width = width
self.height = height
def string(self):
print("{{Width:{}, Height:{}}}".format(self.width, self.height))
class Rectangle(Point, Size):
def __init__(self, x, y, width, height):
Point.__init__(self, x, y)
Size.__init__(self, width, height)
def string(self):
print("該圖形初始化點(diǎn)為:{{X:{}, Y:{}}}; 長寬分別為:{{Width:{}, Height:{}}}".format(self.x, self.y, self.width, self.height))
if __name__ == "__main__":
c = Circle(5, 5, 8)
c.string()
# 該圖形初始化點(diǎn)為:{X:5, Y:5}; {半徑為:8}
r1 = Rectangle(15, 15, 15, 15)
r1.string()
# 該圖形初始化點(diǎn)為:{X:15, Y:15}; 長寬分別為:{Width:15, Height:15}
r2 = Rectangle(40, 30, 11, 14)
# 該圖形初始化點(diǎn)為:{X:40, Y:30}; 長寬分別為:{Width:11, Height:14}
r2.string()
- 重寫對(duì)象的
__str__方法
被@property修飾
class Cat(object):
""" 貓科動(dòng)物 """
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
"""重寫對(duì)象的__str__方法"""
return "我叫: {}, 今年{}歲".format(self.name, self.age)
if __name__ == "__main__":
c = Cat("小黑", 2)
print(c) # 我叫: 小黑, 今年2歲
注意:如果沒有重寫__str__方法,則打印結(jié)果為<__main__.Cat object at 0x10d99c128>。
2.3 類的高級(jí)特性
-
@property和@XXX.setter
將類的方法當(dāng)做屬性來使用。
@property相當(dāng)于javascript中的get方法。
@XXX.setter相當(dāng)于javascript中的set方法
class Cat(object):
""" 貓科動(dòng)物 """
def __init__(self, name, age):
self.name = name
self.__age = age
@property
def age(self):
return self.__age
@age.setter
def age(self, age):
if not isinstance(age, int):
print("年齡只能是整數(shù)")
elif age < 0 or age > 20:
print("年齡只能在0-20之間")
else:
self.__age = age
@property
def show_info(self):
return "我叫: {}, 今年{}歲".format(self.name, self.age)
if __name__ == "__main__":
c = Cat("小黑", 2)
print(c.show_info) # 我叫: 小黑, 今年2歲
c.age = '3' # 年齡只能是整數(shù)
c.age = 26 # 年齡只能在0-20之間
c.age = 6
print(c.show_info) # 我叫: 小黑, 今年6歲
javascript代碼實(shí)現(xiàn)如下:
class Cat {
constructor(name, age) {
this.name = name
this._age = age
}
get age() {
return this._age
}
set age(age){
if(typeof age !== "number") {
console.log("年齡只能是整數(shù)")
} else if(age < 0 || age > 20) {
console.log("年齡只能在0-20之間")
} else {
this._age = age
}
}
get show_info(){
return `我叫: ${this.name}, 今年${this._age}歲`
}
}
c = new Cat("小黑", 2)
console.log(c.show_info) // 我叫: 小黑, 今年2歲
c.age = '3' // 年齡只能是整數(shù)
c.age = 26 // 年齡只能在0-20之間
c.age = 6
console.log(c.show_info) // 我叫: 小黑, 今年6歲
注意:isinstance(age, int/str)判斷是否是數(shù)字/字符串。
參考:《使用@property》
-
__slots__
__slots__可以限制該類可以添加的屬性。__slots__定義的屬性僅對(duì)當(dāng)前類起作用,對(duì)繼承的子類是不起作用的。除非在子類中也定義__slots__。這樣,子類允許定義的屬性就是自身的__slots__加上父類的__slots__。
class Cat(object):
""" 貓科動(dòng)物 """
__slots__ = ('name', 'age')
def __init__(self, name, age):
self.name = name
self.age = age
def show_info(self):
return "我叫: {}, 今年{}歲".format(self.name, self.age)
class HuaCat(Cat):
__slots__ = ('color',)
def eat():
print("我喜歡吃魚")
if __name__ == "__main__":
c = HuaCat("咪咪", 2)
print(c.show_info()) # 我叫: 咪咪, 今年2歲
c.color = '白色' #
print(c.color) # 白色
c.eat = eat # AttributeError: 'Cat' object has no attribute 'eat'
注意:若元組只有一個(gè)元素,則需要加,。
- 靜態(tài)方法和實(shí)例方法
@staticmethod表示靜態(tài)方法,沒有默認(rèn)參數(shù)。
@classmethod表示類方法,第一個(gè)默認(rèn)參數(shù)為該類對(duì)象(cls)。
class Cat(object):
tag = "貓科動(dòng)物" # 靜態(tài)屬性
def __init__(self, name):
self.name = name # 實(shí)例屬性
@staticmethod
def static_fn(arg): # 靜態(tài)方法
"""靜態(tài)方法"""
print("調(diào)用靜態(tài)方法, 參數(shù)為:{}".format(arg))
@classmethod
def class_fn(cls, arg): # 類方法
"""類方法"""
print("調(diào)用類方法, 參數(shù)為:{}".format(arg))
def show_info(self): # 實(shí)例方法
"""實(shí)例方法"""
print("類的屬性: {}, 實(shí)例的屬性: {}".format(self.tag, self.name))
if __name__ == '__main__':
# 類調(diào)用靜態(tài)方法
Cat.static_fn(1) # 調(diào)用靜態(tài)方法, 參數(shù)為:1
c = Cat("小花")
# 實(shí)例調(diào)用靜態(tài)方法
c.static_fn(2) # 調(diào)用靜態(tài)方法, 參數(shù)為:2
# 類調(diào)用類方法
Cat.class_fn(3) # 調(diào)用類方法, 參數(shù)為:3
# 實(shí)例調(diào)用類方法
c.class_fn(4) # 調(diào)用類方法, 參數(shù)為:4
# 實(shí)例調(diào)用實(shí)例方法
c.show_info() # 類的屬性: 貓科動(dòng)物, 實(shí)例的屬性: 小花
注意:類和實(shí)例都可以調(diào)用靜態(tài)方法和類方法。
-
hasattr判斷對(duì)象是否擁有某個(gè)屬性
class Person(object):
def __init__(self, name):
self.name = name
p = Person('小明')
print(hasattr(p, 'name')) # True
print(hasattr(p, 'age')) # False
dict1 = {"a": 1, "b": 2}
print(hasattr(dict1, "a")) # False
print("a" in dict1) # True
注意:hasattr只能判斷對(duì)象,不能判斷字典。
2.4 總結(jié)
- 小結(jié)
面向?qū)ο蟆㈩惻c對(duì)象、多重繼承、類的高級(jí)特性、靜態(tài)方法和實(shí)例方法。
3. 面向?qū)ο髴?yīng)用
3.1 介紹
- 章節(jié)概要
迭代器、生成器、實(shí)戰(zhàn):模擬range函數(shù)效果、異常處理。
3.2 裝飾器的介紹與應(yīng)用
- 裝飾器
是一個(gè)返回函數(shù)的高階函數(shù),用來拓展原來函數(shù)的功能。 - 簡單裝飾器
def log(func):
def wrapper():
print("開始執(zhí)行")
func()
print("結(jié)束執(zhí)行")
return wrapper
def log_in(func):
def wrapper():
print("start...")
func()
print("end...")
return wrapper
@log
@log_in
def hello():
print("hello world")
if __name__ == '__main__':
hello()
# 開始執(zhí)行
# start...
# hello world
# end...
# 結(jié)束執(zhí)行
注釋:@log裝飾hello方法相當(dāng)于hello = log(hello)。
- 裝飾器傳參、被裝飾函數(shù)傳參及返回值
def log(name=None):
def decorator(func):
def wrapper(*args, **kwargs):
print("start call {}...".format(name))
print(args, kwargs)
res = func(*args, **kwargs)
print("end call {}...".format(name))
return res
return wrapper
return decorator
@log('hello')
def hello():
print("hello world")
@log('add')
def add(a, b):
return a + b
if __name__ == '__main__':
hello()
# start call hello...
# () {}
# hello world
# end call hello...
result = add(3, b=5)
# start call add...
# (3,) {'b': 5}
# end call add...
print(result)
# 8
- 帶參數(shù)的裝飾器之
wraps
from functools import wraps
def wrapper1(func):
def wrapper_fn():
"""裝飾器函數(shù)1"""
func()
return wrapper_fn
def wrapper2(func):
@wraps(func)
def wrapper_fn():
"""裝飾器函數(shù)2"""
func()
return wrapper_fn
def fun1():
"""函數(shù)1"""
pass
@wrapper1
def fun2():
"""函數(shù)2"""
pass
@wrapper2
def fun3():
"""函數(shù)3"""
pass
if __name__ == '__main__':
print("doc: {}, name: {}".format(fun1.__doc__, fun1.__name__))
# doc: 函數(shù)1, name: fun1
print("doc: {}, name: {}".format(fun2.__doc__, fun2.__name__))
# doc: 裝飾器函數(shù)1, name: wrapper_fn
print("doc: {}, name: {}".format(fun3.__doc__, fun3.__name__))
# doc: 函數(shù)3, name: fun3
- 類的裝飾器
裝飾器實(shí)現(xiàn)實(shí)現(xiàn)單例模式。
def wrapper(cls):
def wrapper_fn(*args, **kwargs):
if not cls.instance:
cls.instance = cls(*args, **kwargs)
return cls.instance
return wrapper_fn
@wrapper
class Person(object):
instance = None
def __init__(self, name):
self.name = name
def print_info(self):
print(self.name)
if __name__ == "__main__":
p1 = Person('張三')
p1.print_info() # 張三
print(id(p1)) # 4499276296
p2 = Person('李四')
p2.print_info() # 張三
print(id(p2)) # 4499276296
注釋:@wrapper裝飾Person類相當(dāng)于Person = wrapper(Person)。
3.3 迭代器與生成器
- 迭代器(
iterate)介紹
iterate意味著重復(fù)多次。
實(shí)現(xiàn)了__iter__方法的對(duì)象是可迭代的。
實(shí)現(xiàn)了__next__方法的對(duì)象是迭代器。
調(diào)用__next__,迭代器返回下一個(gè)值。
如果迭代器沒有下一個(gè)值了,則觸發(fā)StopIteration異常。 - 簡單迭代器
可以直接使用for...in...循環(huán)的數(shù)據(jù)類型都被稱為可迭代對(duì)象,例如:列表、字典、元組。使用iter()方法把可迭代對(duì)象轉(zhuǎn)換成迭代器。
l = [1, 2, 3, 4]
i = iter(l)
print(i.__next__()) # 1
print(next(i)) # 2
print(i.__next__()) # 3
print(next(i)) # 4
print(i.__next__()) # Error: StopIteration
注釋:i.__next__()和 next(i)方法等價(jià)。
- 自定義迭代器
class PowNumber(object):
"""迭代器,生成1, 2, 3...數(shù)的平方"""
value = 0
def __init__(self, max_value):
self.max_value = max_value
def __next__(self):
self.value += 1
if self.value > self.max_value:
raise StopIteration
return self.value ** 2
def __iter__(self):
return self
if __name__ == '__main__':
p = PowNumber(10)
print(p. __next__()) # 1
print(next(p)) # 4
print(next(p)) # 9
for i in p:
print(i) # 16 25 35...100
注釋:使用p.__next__()或next(p)迭代時(shí),超出最后一個(gè)值報(bào)錯(cuò)。使用for...in...迭代時(shí),超出最后一個(gè)值停止迭代,不報(bào)錯(cuò)。
Python中的raise相當(dāng)于JavaScript中的throw, 用來拋出異常。
注意:使用for...in...循環(huán)既可以遍歷可迭代對(duì)象,又可以遍歷迭代器。
- 生成器介紹
生成器是一種使用普通函數(shù)語法定義的迭代器。
包含yield語句的函數(shù)都被稱為生成器。
不使用return返回一個(gè)值,而是生成多個(gè)值,每次生成一個(gè)。
每次使用yield生成一個(gè)值后,函數(shù)都將凍結(jié)。
被重新喚醒后,函數(shù)將從停止的地方開始繼續(xù)執(zhí)行。
注釋:生成器是一種特殊的迭代器。 - 生成器簡單示例
l1 = [x * x for x in [1, 2, 3, 4, 5]]
print(l1) # [1, 4, 9, 16, 25]
l2 = (x * x for x in [1, 2, 3, 4, 5])
print(l2) # <generator object <genexpr> at 0x106c28408>
print(next(l2)) # 1
print(next(l2)) # 4
print(next(l2)) # 9
for item in l2:
print(item) # 16 25
注意:[x * x for x in [1, 2, 3, 4, 5]]為列表推導(dǎo)式的寫法。
- 自定義生成器
def pow_number1():
for x in [1, 2, 3, 4]:
yield x * x
def pow_number2():
return (x * x for x in [1, 2, 3, 4])
if __name__ == '__main__':
res1 = pow_number1()
print(res1) # <generator object pow_number1 at 0x10a87f408>
for i in res1:
print(i) # 1 4 9 16
res2 = pow_number2()
print(res2) # <generator object pow_number2.<locals>.<genexpr> at 0x10e8437c8>
for i in res2:
print(i) # 1 4 9 16
- 模擬
range函數(shù)
class IterRange(object):
"""使用迭代器模擬range方法"""
def __init__(self, start, end):
self.start = start - 1
self.end = end
def __next__(self):
self.start += 1
if self.start >= self.end:
raise StopIteration
return self.start
def __iter__(self):
return self
class GenRange(object):
"""使用生成器模擬range方法"""
def __init__(self, start, end):
self.start = start - 1
self.end = end
def range(self):
while True:
self.start += 1
if self.start >= self.end:
break
yield self.start
def gen_range(start, end):
"""使用生成器模擬range方法"""
start -= 1
while True:
start += 1
if start >= end:
break
yield start
def range_test1(range):
print(range)
print(list(range))
def range_test2(range):
for i in range:
print(i)
if __name__ == "__main__":
# 測試內(nèi)置range方法
range_test1(range(5, 10)) # range(5, 10) [5, 6, 7, 8, 9]
range_test2(range(5, 10)) # 5 6 7 8 9
# 迭代器模擬的range方法
range_test1(IterRange(5, 10)) # <__main__.IterRange object at 0x10a92f198> [5, 6, 7, 8, 9]
range_test2(IterRange(5, 10)) # 5 6 7 8 9
# 生成器模擬的range方法
range_test1(GenRange(5, 10).range()) # <generator object GenRange.range at 0x10a9057c8> [5, 6, 7, 8, 9]
range_test2(GenRange(5, 10).range()) # 5 6 7 8 9
range_test1(gen_range(5, 10)) # <generator object gen_range at 0x10a9057c8> [5, 6, 7, 8, 9]
range_test2(gen_range(5, 10)) # 5 6 7 8 9
3.4 實(shí)戰(zhàn)-文件備份
- 代碼演示
import os
class FileBackup(object):
"""文件備份"""
def __init__(self, src, dist, file_type):
"""
構(gòu)造方法
:param src: 源目錄
:param dist: 目標(biāo)目錄
:param file_type: 備份文件類型
"""
self.src = src
self.dist = dist
self.type = file_type
def backup(self):
"""
拷貝src目錄下所有層級(jí)指定類型文件
"""
self.backup_dir(self.src, self.dist)
def backup_dir(self, src_dir_path, dist_dir_path):
"""
備份某一目錄下所有層級(jí)指定類型文件
:param src_dir_path: 源文件夾
:param dist_dir_path: 目標(biāo)文件夾
:return:
"""
if not os.path.exists(dist_dir_path):
os.makedirs(dist_dir_path)
file_names = os.listdir(src_dir_path)
for file_name in file_names: # 遍歷目錄中所有文件/文件夾
src_file_path = os.path.join(src_dir_path, file_name)
dist_file_path = os.path.join(dist_dir_path, file_name)
if os.path.isfile(src_file_path): # 是文件
if os.path.splitext(src_file_path)[-1].lower() == self.type: # 是指定文件類型
print(">>> {} 開始備份".format(file_name))
self.backup_file(src_file_path, dist_file_path)
print(">>> {} 備份完成".format(file_name))
else: # 不是指定文件類型
print("{} 文件類型不符合要求,跳過".format(file_name))
else: # 是文件夾
self.backup_dir(src_file_path, dist_file_path)
@staticmethod
def backup_file(src_file_path, dist_file_path):
"""
備份文件
:param src_file_path: 源文件路徑
:param dist_file_path: 目標(biāo)文件路徑
:return:
"""
with open(src_file_path, 'rb') as file, open(dist_file_path, 'wb') as file_dist:
while True:
rest = file.read(100)
if not rest:
break
file_dist.write(rest)
file_dist.flush()
if __name__ == '__main__':
base_path = os.path.dirname(__file__)
src_path = os.path.join(base_path, 'src')
dist_path = os.path.join(base_path, 'dist')
bak = FileBackup(src_path, dist_path, '.png')
bak.backup()
語法:with open('path1') as file1, open('path2') as file2:。
注意:除文本文件之外,讀取其他格式文件必須使用二進(jìn)制模式b。
3.5 異常處理
- 異常概述
異常是某個(gè)類的實(shí)例,有一些內(nèi)置的異常類。 - 內(nèi)置的異常類
Exception- 幾乎所有異常都是從它派生出來的
AttributeError- 引用屬性或?qū)傩再x值失敗時(shí)觸發(fā)
OSError- 操作系統(tǒng)不能執(zhí)行指定任務(wù)(例如打開文件)時(shí)觸發(fā)
IndexError- 操作序列中不存在的索引時(shí)觸發(fā)
KeyError- 操作映射中不存在的鍵時(shí)觸發(fā)
NameError- 找不到變量時(shí)觸發(fā)
SyntaxError- 代碼不正確時(shí)觸發(fā)
TypeError- 將內(nèi)置操作或函數(shù)用于類型不正確的對(duì)象時(shí)觸發(fā)
ValueError- 將內(nèi)置操作或函數(shù)用于類型正確但包含的值不合適時(shí)觸發(fā)
ZeroDivisionError- 除法或求模運(yùn)算時(shí)第二個(gè)參數(shù)為零時(shí)觸發(fā) - 異常捕獲
def test_div(num1, num2):
return num1 / num2
if __name__ == '__main__':
# 一次捕獲所有異常
try:
rest = test_div(5, 0)
print("{}結(jié)果是".format(rest))
except Exception as err:
print(err) # 出錯(cuò)了
# 一次捕獲某一個(gè)異常
try:
rest = test_div(5, 0)
print("{}結(jié)果是".format(rest))
except ZeroDivisionError:
print("報(bào)錯(cuò)了,除數(shù)不能為0") # 報(bào)錯(cuò)了,除數(shù)不能為0
except TypeError:
print("報(bào)錯(cuò)了,請(qǐng)輸入數(shù)字")
# 一次捕獲多個(gè)異常
try:
rest = test_div(5, '0')
print("{}結(jié)果是".format(rest))
except (ZeroDivisionError, TypeError):
print("異常了") # 異常了
# 捕獲并打印異常
try:
rest = test_div(5, 0)
print("{}結(jié)果是".format(rest))
except (ZeroDivisionError, TypeError) as err:
print(err) # division by zero
# finally語法
try:
rest = test_div(5, '1')
except Exception as err:
print(err) # unsupported operand type(s) for /: 'int' and 'str'
finally:
print("finally")
- 異常應(yīng)用
try:
f = open("1.txt", 'r')
rest = f.read()
print(rest)
except Exception as err:
print(err) # [Errno 2] No such file or directory: '1.txt'
finally:
try:
f.close()
print('closed')
except Exception as err:
print(err) # name 'f' is not defined
注意:如果文件不存在的話,open()和close()都會(huì)拋出異常,所以都要加異常捕獲。
- 自定義異常
通過繼承Exception類來自定義異常。
class ApiException(Exception):
def __init__(self, code, msg):
self.code = code
self.msg = msg
def __str__(self):
return "Error: {} - {}".format(self.code, self.msg)
def divide(num1, num2):
"""除法實(shí)現(xiàn)"""
if not isinstance(num1, int) or not isinstance(num2, int):
raise ApiException('400001', '兩個(gè)參數(shù)必須都是整數(shù)')
if num2 == 0:
raise ApiException('400002', '除數(shù)不能為0')
return num1 / num2
if __name__ == "__main__":
try:
print(divide(5, 0))
except ApiException as err:
print(err) # Error: 400002 - 除數(shù)不能為0
- 異常的傳遞
如果異常不被捕獲,那么它會(huì)一層一層的向上傳遞。
def my_for():
for i in range(1, 10):
print(i)
if i == 5:
raise Exception('這是一個(gè)異常')
def do_sth():
my_for()
def test_trans():
do_sth()
if __name__ == '__main__':
try:
test_trans()
except Exception as err:
print(err) # 1 2 3 4 5 這是一個(gè)異常