Python基礎(chǔ)入門 - 面向?qū)ο?/h2>

1. 初識(shí)面向?qū)ο?/h1>

1.1 介紹

  1. 步驟介紹
    面向?qū)ο蟮母攀?br> 面向?qū)ο蟮膶?shí)現(xiàn)
    面向?qū)ο蟮膽?yīng)用
    內(nèi)存管理
    進(jìn)程、線程、協(xié)程
  2. 概要
    面向?qū)ο蟮娜筇卣?br> 面向?qū)ο笕筇卣鞯呐e例
  3. 目標(biāo)
    了解面向?qū)ο缶幊痰乃悸?br> 了解面向?qū)ο笈c面向過程的區(qū)別
    了解面向?qū)ο缶幊痰膬?yōu)點(diǎn)
    了解面向?qū)ο蟮娜筇卣?/li>

1.2 什么是面向?qū)ο?/h2>
  1. 類與對(duì)象
    類是抽象的概念。
    對(duì)象是類的實(shí)例。
  2. 封裝
    隱藏實(shí)現(xiàn)細(xì)節(jié),提供訪問接口。
  3. 繼承
    一種類與類之間的派生關(guān)系。
  4. 多態(tài)
    父類引用指向子類對(duì)象。

2. 類的特性

2.1 類的定義與實(shí)現(xiàn)

  1. 類的方法
    dir 列出類的所有屬性及方法
    help 查看類、方法的幫助信息
    __init__ 構(gòu)造方法
    __del__ 析構(gòu)函數(shù)
    __doc__ 顯示類的文檔注釋字符串
    __module__ 顯示類所在的模塊名
    __bases__ 一個(gè)元組顯示類所繼承的所有父類
  2. 在編寫代碼時(shí)只寫框架思路,具體實(shí)現(xiàn)還未編寫就可以用 pass 進(jìn)行占位,使程序不報(bào)錯(cuò),不會(huì)進(jìn)行任何操作。
x = 5

if x < 6:
    pass
  1. 判斷是否是某個(gè)類的實(shí)例
    isinstance(instance, Class)
  2. 類與對(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. 方法介紹
    (1) 判斷是否是父子類關(guān)系
    issubclass(Child, Parent)
    (2) 子類調(diào)用父類方法
def eat(self):
    super(Cat, self).eat()
    print("貓還喜歡吃魚")
  1. 繼承示例
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)物都吃東西 貓還吃魚
  1. 多重繼承
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)用位置靠前的父類的方法。

  1. 如果輸出內(nèi)容含有花括號(hào)時(shí),需要加一層{}
print("{{{}}}".format("字符串")) # {字符串}
  1. 子類調(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
  1. 圖形面向?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()
  1. 重寫對(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í)特性

  1. @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》

  1. __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è)元素,則需要加,

  1. 靜態(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)方法和類方法。

  1. 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é)

  1. 小結(jié)
    面向?qū)ο蟆㈩惻c對(duì)象、多重繼承、類的高級(jí)特性、靜態(tài)方法和實(shí)例方法。

3. 面向?qū)ο髴?yīng)用

3.1 介紹

  1. 章節(jié)概要
    迭代器、生成器、實(shí)戰(zhàn):模擬range函數(shù)效果、異常處理。

3.2 裝飾器的介紹與應(yīng)用

  1. 裝飾器
    是一個(gè)返回函數(shù)的高階函數(shù),用來拓展原來函數(shù)的功能。
  2. 簡單裝飾器
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)。

  1. 裝飾器傳參、被裝飾函數(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
  1. 帶參數(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
  1. 類的裝飾器
    裝飾器實(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 迭代器與生成器

  1. 迭代器(iterate)介紹
    iterate意味著重復(fù)多次。
    實(shí)現(xiàn)了__iter__方法的對(duì)象是可迭代的。
    實(shí)現(xiàn)了__next__方法的對(duì)象是迭代器。
    調(diào)用__next__,迭代器返回下一個(gè)值。
    如果迭代器沒有下一個(gè)值了,則觸發(fā)StopIteration異常。
  2. 簡單迭代器
    可以直接使用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à)。

  1. 自定義迭代器
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ì)象,又可以遍歷迭代器。

  1. 生成器介紹
    生成器是一種使用普通函數(shù)語法定義的迭代器。
    包含yield語句的函數(shù)都被稱為生成器。
    不使用return返回一個(gè)值,而是生成多個(gè)值,每次生成一個(gè)。
    每次使用yield生成一個(gè)值后,函數(shù)都將凍結(jié)。
    被重新喚醒后,函數(shù)將從停止的地方開始繼續(xù)執(zhí)行。
    注釋:生成器是一種特殊的迭代器。
  2. 生成器簡單示例
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)式的寫法。

  1. 自定義生成器
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
  1. 模擬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)-文件備份

  1. 代碼演示
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 異常處理

  1. 異常概述
    異常是某個(gè)類的實(shí)例,有一些內(nèi)置的異常類。
  2. 內(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ā)
  3. 異常捕獲
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")
  1. 異常應(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ì)拋出異常,所以都要加異常捕獲。

  1. 自定義異常
    通過繼承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
  1. 異常的傳遞
    如果異常不被捕獲,那么它會(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è)異常
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容