Python基礎第二週--程序設計與數據結構

二 程序設計與數據結構

1 萬物皆對象、Python不例外

1.1 面向對象編程基本概念:

  • Python從設計之初就已經是一門面向對象的語言,比如Java, C#, Javascript,C++都是典型的面嚮對象語言.C語言和彙編語言是典型的面向過程語言.
  • 面向過程的語言更傾向於功能的實現與封裝,體現計算機實現功能
  • 面向對象的語言更傾向於業務層面的流程實現,體現解決具體事物流程
  • 現代計算機程序都是面向過程和麪向對象的結合使用,面向對象同樣也支持面向過程

1.1.1 面向對象技術簡介

  • 類(Class): 用來描述具有相同的屬性和方法的對象的集合。它定義了該集合中每個對象所共有的屬性和方法。對象是類的實例。
  • 類變量:類變量在整個實例化的對象中是公用的。類變量定義在類中且在函數體之外。類變量通常不作爲實例變量使用。
  • 數據成員:類變量或者實例變量用於處理類及其實例對象的相關的數據。
  • 方法重寫:如果從父類繼承的方法不能滿足子類的需求,可以對其進行改寫,這個過程叫方法的覆蓋(override),也稱爲方法的重寫。
  • 實例變量:定義在方法中的變量,只作用於當前實例的類。
  • 繼承:即一個派生類(derived class)繼承基類(base class)的字段和方法。繼承也允許把一個派生類的對象作爲一個基類對象對待。例如,有這樣一個設計:一個Dog類型的對象派生自Animal類,這是模擬"是一個(is-a)"關係(例圖,Dog是一個Animal)。
  • 實例化:創建一個類的實例,類的具體對象。
  • 方法:類中定義的函數。
  • 對象:通過類定義的數據結構實例。對象包括兩個數據成員(類變量和實例變量)和方法。

1.2 類和對象:

1.2.1 類定義:

class ClassName:
    <statement-1>
    .
    .
    
    <statement-N>

1.2.2 類的使用–實例化類的對象:

#!/usr/bin/python3

# 類的定義
class MyClass:
    i = 12345
    def f(self):
        return 'hello world'

# 實例化類
x = MyClass()

# 訪問類的屬性和方法
print("MyClass 類的屬性 i 爲:", x.i)
print("MyClass 類的方法 f 輸出爲:", x.f())

# 結果:
# MyClass 類的屬性 i 爲: 12345
# MyClass 類的方法 f 輸出爲: hello world

1.3 類的組成–實例屬性和方法:

  • 類由三個部分組成
    1. 實例屬性或者叫對象屬性 或簡稱屬性
    2. 方法–可理解爲在面向過程中定義的函數, 但是首個參數必須是‘self’
    3. 類屬性–沒有前兩個那麼常用和重要
  • 代碼示例:
# 實例屬性  必須通過初始化或者實例化對象,通過對象去訪問,
class Student(object):
    def __init__(self, name):
        self.name = name
s = Student('Bob')
print(s.name )

# 類屬性  不需要實例化對象,直接通過類名訪問
class Student(object):
    name = 'Student'
print(Student.name)


# 面向對象
# 屬性和方法
class Student(object):
    # 初始化方法  self指向創建的實例本身
    def __init__(self,name,score):
        self.name = name
        self.score = score

    def print_score(self):
        print('%s: %s' % (self.name,self.score))

# 實例化對象1
xiaohong = Student('xiaohong',98)
print(id(xiaohong))
# 實例化對象2
xiaobai = Student('xiaobai',81)
print(id(xiaobai))
# 實例化對象3
xiaobai = Student('xiaobai',81)
print(id(xiaobai))

1.4 構造方法–對象方法中的特殊方法:

  • 很多類都傾向於將對象創建爲有初始狀態的。因此類可能會定義一個名爲 init() 的特殊方法(構造方法),像下面這樣:
def __init__(self):
    self.data = []
  • 類定義了 init() 方法的話,類的實例化操作會自動調用 init() 方法。所以在下例中,可以這樣創建一個新的實例:
x = MyClass()
  • 當然, init() 方法可以有參數,參數通過 init() 傳遞到類的實例化操作上。例如:
#!/usr/bin/python3

class Complex:
    def __init__(self, realpart, imagpart):
        self.r = realpart
        self.i = imagpart
x = Complex(3.0, -4.5)
print(x.r, x.i)   # 輸出結果:3.0 -4.5

1.4.1 self關鍵字–代表類的實例,而非類

  • 方法與普通的函數只有一個特別的區別——它們必須有一個額外的第一個參數名稱, 按照慣例它的名稱是 self。
class Test:
    def prt(self):
        print(self)
        print(self.__class__)

t = Test()
t.prt()
  • 以上實例執行結果爲:
<__main__.Test instance at 0x100771878>
__main__.Test
  • 從執行結果可以很明顯的看出,self 代表的是類的實例,代表當前對象的地址,而 self.class 則指向類。
  • self 不是 python 關鍵字,我們可以換成其他變量名

1.5 總結

1.5.1 面向對象編程概括

  • 面向對象編程是一種程序設計思想
  • 面向對象把類和對象作爲程序的基本單元
  • 對象包含屬性和方法
  • 面向過程編程爲:函數的調用集合
  • 面向對象編程爲:對象之間傳遞信息的集合
  • 處處皆對象

1.5.2 類和實例總結

  • 類可以理解爲圖紙或者模版

  • 實例是根據類的圖紙或者模版創建出來的一個一個對象

  • 類定義class,關鍵字self

  • 類的初始化函數__init__

  • 面向對象三大特點:繼承,封裝,多態

  • 屬性和方法

1.6 訪問限制

  • 通過”__”兩個下劃線可以修飾私有變量
  • 通過編寫get和set方法來修改對象的屬性
  • Python中沒有真正的私有屬性和私有方法
class Student(object):
    # 方法  self指向創建的實例本身
    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'

    def set_score(self,score):
        if  0<=score<=100:
            self.__score=score
        else:
            raise ValueError('分數請大於0小於等於100')

    def get_score(self):
        return self.__score

xiaolv = Student('xiaolv',45)


print(xiaolv.set_score(98))
print(xiaolv.get_score())
# python沒有真正的是私有,把私有改名稱_Student__name
print(xiaolv._Student__name)

2 面向對象—繼承和多態

2.1 繼承

  • 定義一個class的時候,可以從某個現有的class繼承,新的 class稱爲子類(Subclass)
  • 被繼承的class稱爲基類、父類或超類(Base class、Super class)
class Animal(object):
    def run(self):
        print('Animal is running')

class Dog(Animal):
    pass

class Cat(Animal):
    pass

dog = Dog()
# dog.run()

cat = Cat()
# cat.run()

2.1.1 多繼承【擴展】

  • Python同樣有限的支持多繼承形式。

  • 需要注意圓括號中父類的順序,若是父類中有相同的方法名,而在子類使用時未指定,python從左至右搜索 即方法在子類中未找到時,從左到右查找父類中是否包含方法。


#類定義
class People:
    #定義基本屬性
    name = ''
    age = 0
    #定義私有屬性,私有屬性在類外部無法直接進行訪問
    __weight = 0
    #定義構造方法
    def __init__(self,n,a,w):
        self.name = n
        self.age = a
        self.__weight = w
    def speak(self):
        print("%s 說: 我 %d 歲。" %(self.name,self.age))

#單繼承示例
class Student(people):
    grade = ''
    def __init__(self,n,a,w,g):
        #調用父類的構函
        people.__init__(self,n,a,w)
        self.grade = g
    #覆寫父類的方法
    def speak(self):
        print("%s 說: 我 %d 歲了,我在讀 %d 年級"%(self.name,self.age,self.grade))

#另一個類,多重繼承之前的準備
class Speaker():
    topic = ''
    name = ''
    def __init__(self,n,t):
        self.name = n
        self.topic = t
    def speak(self):
        print("我叫 %s,我是一個演說家,我演講的主題是 %s"%(self.name,self.topic))

#多重繼承
class Sample(speaker,student):
    a =''
    def __init__(self,n,a,w,g,t):
        student.__init__(self,n,a,w,g)
        speaker.__init__(self,n,t)

test = Sample("Tim",25,80,4,"Python")
test.speak()   #方法名同,默認調用的是在括號中排前地父類的方法


# 執行以上程序輸出結果爲:
# 我叫 Tim,我是一個演說家,我演講的主題是 Python

2.2.2 方法重寫

  • 如果你的父類方法的功能不能滿足你的需求,你可以在子類重寫你父類的方法
class Animal(object):
    def run(self):
        print('Animal is running')

class Dog(Animal):
    def run(self): # 重寫
        # 調用父類的run方法
        # super().run()
        print('Dog is running')

    def eat(self):
        print('eat meat')

dog = Dog()
dog.run()

class Cat(Animal):
    def run(self): # 重寫
        # 調用父類的run方法
        # super().run()
        print('Cat is running')

    def eat(self):
        print('eat fish')

2.3 多態

  • 代碼運行時才確定對象的具體類型

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Vnmo5Q24-1573464810584)(image/1572921614914.png)]

b=Animal() # b是Animal類型
c=Dog() # c是Dog類型


print(isinstance(b, Animal))
print(isinstance(c, Dog))
print(isinstance(c, Animal))
print(isinstance(b, Dog))

# 調用run_twice方法,需要傳遞Animal類的對象或者Animal的子類
def run_twice(animal):
    animal.run()
    animal.run()


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

run_twice(Animal())

2.4 類型判斷

  • type()
  • isinstance()
  • dir() #獲得一個對象的所有屬性和方法

import types
def fn():
    pass

print(type(fn)==types.FunctionType)

print(type(abs)==types.BuiltinFunctionType)

# getattr(),setattr(),hasattr()

class MyObject(object):
    def __init__(self):
        self.x = 9

    def power(self):
        return self.x * self.x
obj = MyObject()

# obj 有屬性'x'嗎?
print(hasattr(obj, 'x'))
print(obj.x)

# 設置一個屬性'y'
setattr(obj, 'y', 19)
print(hasattr(obj, 'y'))
print(getattr(obj, 'y'))

# dir()
print(obj.__dir__())
print(dir(obj))

a = [1,2]
print(len(a))
print(a.__len__())

2.5 封裝

  • 通過類的實例直接訪問修改對象屬性,在實際的開發過程中會造成代碼的安全性和健壯性受到影響
  • 通過把對象屬性私有化, 不直接提供對外部的直接訪問. 如果外部需要修改或者獲取屬性值,可以通過建立set和get方法來間接修改屬性,叫屬性的封裝.
  • 例子:參看4.5.1

3 設計模式

3.1 列表生成式

  • Python內置的非常簡單卻強大的可以用來創建list的生成式
  • 快速的把字典內容轉變成list
d = {'x': 'A', 'y': 'B', 'z': 'C' }
print([k + '=' + v for k, v in d.items()])

# 生成list,[1,2,3...10]

print(list(range(1, 11)))
# 生成[1*1,2*2,3*3...]

print([x*x for x in range(1, 11)])

# 'abc','123' 輸出a1,a2,a3,b1,b2,b3
print([m + n for m in 'abc' for n in '123'])

print([m + n for m in 'a.b.c' for n in '123' if m != '.'])

3.2 生成器

  • 循環的過程中不斷推算出後續的元素呢?這樣就不必創建完整 的list,這種一邊循環一邊計算的機制,稱爲生成器: generator
  • 兩種定義
    • 列表生成式
    • 帶yield的generator function
g = (x * x for x in range(10))
print(g)

print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))

for n in g:
    print(n)

# yield
# 斐波那契數列 1,1,2,3,5,8
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        print(b)
        a, b = b, a + b
        n = n + 1
    return 'done'

fib(6)

# 輸出關鍵字換成yield
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1
    return 'done'

g = fib(6)

print(next(g))
print(next(g))
print(next(g))

for n in g:
    print(n)

3.2.1 Python中yield關鍵字的使用

  • yield 是一個類似 return 的關鍵字,只是這個函數返回的是個生成器
  • 當你調用這個函數的時候,函數內部的代碼並不立馬執行 ,這個函數只是返回一個生成器對象
  • 當你使用for進行迭代的時候,函數中的代碼纔會執行
  • 生成器特點:可迭代;只能讀取一次;實時生成數據,不全存在內存中。

3.3 迭代器

  • for循環可以應用下列類型:
    • 集合數據類型,list,tuple,dict,set,str等
    • generator ,生成器和帶yield的generator function等
  • 直接可作用於for循環的叫可迭代對象:Iterable
  • 直接可作用於next方法的叫可以生成器對象:Iterator
  • 生成器可以同時作用於for循環和next()函數,不斷調用, 直到拋出StopIteration錯誤
# 判斷一個對象是否可迭代 Iterable
from collections import Iterable
# print(isinstance([],Iterable))
# print(isinstance({},Iterable))
# print(isinstance('abc',Iterable))
# print(isinstance(123,Iterable))


# 判斷一個對象是否是生成器 Iterator
from collections.abc import Iterator
# print(isinstance([],Iterator))
# print(isinstance({},Iterator))
# print(isinstance('abc',Iterator))
# print(isinstance(123,Iterator))

print(isinstance(iter([]),Iterator))
print(isinstance(iter({}),Iterator))
print(isinstance(iter('abc'),Iterator))

4 函數的高級應用

4.1 函數即變量

  • 變量可以指向函數
  • 函數名也是變量
  • 函數可以作爲參數傳入到方法內部
  • 函數本身也可以賦值給變量,即:變量可以指向函數
# abs(-10)函數調用,abs是函數本身
print(abs(-10))

f = abs
print(f)
print(f(-10))

4.2 Python內置的map函數

  • map()函數接收兩個參數,一個是函數,一個是Iterable
  • map將傳入的函數依次作用到序列的每個元素,並把結果作爲 新的Iterator返回
l = [1,2,3,4,5,6,7,8,9]

def f(x):
    return x*x

m = map(f,l)
print(m)
# print(list(m))

# for n in m:
#     print(n)

print(next(m))
print(next(m))
print(next(m))


# l 列表中每個int轉換成字符串
l = [1, 2, 3, 4, 5, 6, 7, 8, 9]

print(list(map(str,l)))

4.3 Python內置的reduce函數

  • reduce把一個函數作用在一個序列[x1, x2, x3, …]上 ,reduce 把結果繼續和序列的下一個元素做累積計算
  • reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
l = [4,2,5,7,8,9]  #獲得425789

from functools import reduce

def f(x, y):
    return x * 10 + y

print(reduce(f,l))
print(type(reduce(f,l)))

4.4 練習

# str()——》int()
 '5632'——》5632  
    
# 思路 
# 1. 字符串每個元素取出來,轉化成對應的數字,得到一個數字序列
# 2. 通過數字序列每兩個*10 相加,得到一個整數

def f(x,y):
    return x * 10 + y

def char2num(s):
    digits = {'0': 0, '1': 1, '2' :2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
    return digits[s]

s1 = '5632'
nums = reduce(f,map(char2num, s1))

print(type(nums))
print(nums) 

4.5 裝飾器

  • python裝飾器就是用於拓展原來函數功能的一種函數,這個函數的特殊之處在於它的返回值也是一個函數,
  • 使用python裝飾器的好處就是在不用更改原函數的代碼前提下給函數增加新的功能
import datetime

# 裝飾器 以一個函數作爲參數,並返回一個函數

def log(f):
    def write_log(*args, **kw):
        with open('./a.txt', 'w') as f1:
            f1.write(f.__name__)
            print('寫入日誌成功,函數名字是:%s'%f.__name__)
            return f(*args, **kw)
    return write_log

@log
def now():
    print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))

4.5.1 Python內置的裝飾器 @property @setter實現對象屬性的封裝

  • @property 使調用類中的方法像引用類中的字段屬性一樣。
  • @property 負責裝飾一個對象函數,讓其生成對應的setter和getter函數,調用的時候,直接可以使用對象名.函數名這種類似於屬性的調用方式來執行函數
class Student(object):
    def __init__(self, score):
        self.__score = score

    def get_score(self):
         return self.__score

    def set_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

stu1 = Student(90)

print(stu1.set_score(85))
print(stu1.get_score())


# 加上裝飾器,方法變屬性
class Student(object):
    def __init__(self, score):
        self.__score = score

    @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


stu2 = Student('67')

stu2.score = 59
print(stu2.score)

4.5.2 靜態方法與類方法

  • @staticmethod 將類中的方法裝飾爲靜態方法,即類不需要創建實例的情況下,可以通過類名直接引用。到達將函數功能與實例解綁的效果。
class TestClass:
    name = "test"

    def __init__(self, name):
        self.name = name

    @staticmethod
    def fun(self, x, y):
        return  x + y

cls = TestClass("felix")
print ("通過實例引用方法")
print (cls.fun(None, 2, 3)) # 參數個數必須與定義中的個數保持一致,否則報錯

print ("類名直接引用靜態方法")
print (TestClass.fun(None, 2, 3))# 參數個數必須與定義中的個數保持一致,否則報錯

  • @classmethod 類方法的第一個參數是一個類,是將類本身作爲操作的方法。類方法被哪個類調用,就傳入哪個類作爲第一個參數進行操作。
class Car(object):
    car = "audi"

    @classmethod
    def value(self, category): # 可定義多個參數,但第一個參數爲類本身
        print ("%s car of %s" % (category, self.car)class BMW(Car):
    car = "BMW"

class Benz(Car):
    car = "Benz"

print ("通過實例調用")
baoma = BMW()
baoma.value("Normal") # 由於第一個參數爲類本身,調用時傳入的參數對應的時category

print ("通過類名直接調用")
Benz.value("SUV")

5 異常處理

5.1 異常介紹:

  • 即便Python程序的語法是正確的,在運行它的時候,也有可能發生錯誤。運行期檢測到的錯誤被稱爲異常。
  • 大多數的異常都不會被程序處理,都以錯誤信息的形式展現在這裏:
  • 異常以不同的類型出現,這些類型都作爲信息的一部分打印出來: 例子中的類型有 ZeroDivisionError,NameError 和 TypeError
>>> 10 * (1/0)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
ZeroDivisionError: division by zero
>>> 4 + spam*3
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
NameError: name 'spam' is not defined
>>> '2' + 2
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: Can't convert 'int' object to str implicitly
  • Python的一些內建異常:
Exception       常規錯誤的基類
AttributeError  對象沒有這個屬性
IOError         輸入/輸出操作失敗
IndexError      序列中沒有此索引(index)
KeyError        映射中沒有這個鍵
NameError       未聲明/初始化對象 (沒有屬性)
SyntaxError     Python 語法錯誤
TypeError       對類型無效的操作
ValueError      傳入無效的參數
ZeroDivisionError   除(或取模)零 (所有數據類型)

5.2 使用異常可以讓你的程序繼續執行:

  • 沒有異常處理的:特點是出現異常會終止程序執行
print("start.....")
x = int(input("Please enter a number: "))
print("number:",x)
print("ok....")
print("end.....")
  • 使用了有異常處理的代碼,程序會執行到最後
print("start.....")
try:
    x = int(input("Please enter a number: "))
    print("number:",x)
    print("ok....")
except ValueError:
    print("Oops!  That was no valid number.  Try again")
print("end.....")
  • try語句按照如下方式工作;
    • 首先,執行try子句(在關鍵字try和關鍵字except之間的語句)
    • 如果沒有異常發生,忽略except子句,try子句執行後結束。
    • 如果在執行try子句的過程中發生了異常,那麼try子句餘下的部分將被忽略。如果異常的類型和 except 之後的名稱相符,那麼對應的except子句將被執行。最後執行 try 語句之後的代碼。
    • 如果一個異常沒有與任何的except匹配,那麼這個異常將會傳遞給上層的try中。
  • 一個 try 語句可能包含多個except子句,分別來處理不同的特定的異常。最多隻有一個分支會被執行。
  • 處理程序將只針對對應的try子句中的異常進行處理,而不是其他的 try 的處理程序中的異常。
  • 一個except子句可以同時處理多個異常,這些異常將被放在一個括號裏成爲一個元組,例如:
    except (RuntimeError, TypeError, NameError):
        pass
  • 最後一個except子句可以忽略異常的名稱,它將被當作通配符使用。你可以使用這種方法打印一個錯誤信息,然後再次把異常拋出。
import sys

try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except OSError as err:
    print("OS error: {0}".format(err))
except ValueError:
    print("Could not convert data to an integer.")
except:
    print("Unexpected error:", sys.exc_info()[0])
    raise

5.3:使用異常可以定位問題原因

  • 一個 try 語句可能包含多個except子句,分別來處理不同的特定的異常。最多隻有一個分支會被執行
  • 最後一個except子句可以忽略異常的名稱,它將被當作通配符使用
print("start.....")
try:
    x = int(input("Please enter a number: "))
    print("number:",x)
    print(100/x)
    print("ok....")
except ValueError:
    print("非純數字錯誤!")
except ZeroDivisionError:
    print("不可以爲零錯誤!")
except:
    print("可選的未知錯誤!")
print("end.....")
  • 一個except子句可以同時處理多個異常,這些異常將被放在一個括號裏成爲一個元組
print("start.....")
try:
    x = int(input("Please enter a number: "))
    print("number:",x)
    print(100/x)
    print("ok....")
except (ValueError,ZeroDivisionError):
    print("非純數字或不可以爲零錯誤!")
except:
    print("可選的未知錯誤!")
    raise   #重新拋出這個異常
print("end.....")

5.4 拋出異常(發現問題讓其他函數解決):

  • Python 使用 raise 語句拋出一個指定的異常。例如:
>>> raise NameError('HiThere')
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
NameError: HiThere
  • raise 唯一的一個參數指定了要被拋出的異常。它必須是一個異常的實例或者是異常的類(也就是 Exception 的子類)。
  • 如果你只想知道這是否拋出了一個異常,並不想去處理它,那麼一個簡單的 raise 語句就可以再次把它拋出。
>>> try:
        raise NameError('HiThere')
    except NameError:
        print('An exception flew by!')
        raise

An exception flew by!
Traceback (most recent call last):
  File "<stdin>", line 2, in ?
NameError: HiThere

5.5 使用異常可以定位到代碼執行中出現問題的函數

  • 代碼順序執行,函數之間互相調用,如果發生錯誤。我們並不知道那裏發生了錯誤,需要測試一下我們的代碼。
def foo(s):
    return 100/int(s)

def bar(s):
    return foo(s)*2

# 在python中,如果py是單獨運行,__name__=='__main__', \
# 如果py文件被其他模塊引入調用的時候就不等於 import
if __name__ == '__main__':
    '''
        測試 給我們自己調用
    '''
    try:
        bar('0')
    except Exception as e:
        print('Error:',e)
    finally:
        print('finally...')

6 第三方模塊的安裝與調用

6.1 自定義Python模塊

  • 在導入一個包的時候,Python 會根據 sys.path 中的目錄來尋找這個包中包含的子目錄。
  • 目錄只有包含一個叫做__init__.py 的文件纔會被認作是一個包,主要是爲了避免一些濫俗的名字(比如叫做 string)不小心的影響搜索路徑中的有效模塊。
  • 最簡單的情況,放一個空的 :file:__init__.py就可以了。當然這個文件中也可以包含一些初始化代碼或者爲(將在後面介紹的)__all__變量賦值。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-EPJPxMYH-1573464810589)(image/1572934201134.png)]

6.2 模塊的使用

  • 一個.Py文件稱之爲一個模塊(Module)
  • 好處:
    • 便於代碼維護,把很多函數放到不同文件,一個.py文件 的 代碼數量少
    • 一個模塊可以被其他地方引用,代碼不必從零寫起
    • 使用模塊還可以避免函數名和變量名衝突。相同名字的函數和變量完全可以分別存在不同的模塊中

6.2.1 模塊的導入與使用

import 語句:
  • 想使用 Python 源文件,只需在另一個源文件裏執行 import 語句
import time

# 格式化成2016-03-20 11:45:39形式
print (time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
from…import 語句:
  • Python的from語句讓你從模塊中導入一個指定的部分到當前命名空間中
from time import strftime,localtime

# 格式化成2016-03-20 11:45:39形式
print (strftime("%Y-%m-%d %H:%M:%S", localtime()))
模塊的使用
  • 模塊分內置模塊和第三方模塊
  • 好處Python解釋器把一個特殊變量__name__置爲__main__, 而如果在其他地方導入該hello模塊時,if判斷將失敗
  • 使用import hello

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-FH2yJvOt-1573464810591)(image/1572935068362.png)]

6.3 第三方模塊的安裝

用pip命令安裝

  • pip install 模塊名
  • pip uninstall 模塊名
  • pip freeze --查看都安裝了哪些模塊名

whl下載安裝的方式

  • 網址: https://www.lfd.uci.edu/~gohlke/pythonlibs/ 下載

  • 安裝:pip install 文件包名

  • 使用Python的另一個發行版本,anaconda

  • Pycharm 裏面安裝

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-5XacUGWW-1573464810592)(image/1572934799360.png)]

7 字符編碼

7.1 字符編碼類型

  • ASCII編碼
  • 中文GB2312
  • Unicode
  • UTF8
print(ord('A'))
print(ord('我'))
print(chr(65))
print(chr(25105))
# 傳輸和保存  需要對字符進行 編解碼,utf-8通用編解碼
x = b'ABC' # 字節數組,byte數組
print(x)
# 中文編碼範圍超過了ASC編碼範圍,報錯
# x = b'你好' # 字節數組,byte數組
# print(x)
# 在bytes中,無法顯示ASC字符的字節,用\x##來顯示
print('你好'.encode('gbk'))
print('ABC'.encode('utf-8'))
print(b'\xc4\xe3\xba\xc3'.decode('gbk'))
# 中文佔用多少字節
print(len('你好AB'))
print(len('你好'.encode('utf-8')))
print(len('你好'.encode('gbk')))

7.2 字符串編碼轉化

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-cHETpCcj-1573464810593)(image/1572936113399.png)]

8 案例實戰:圖片和視頻轉字符動畫

  • 需求:
    • 1.把一張圖片,轉化爲字符畫
    • 2.把一個視頻,轉化成字符動畫
  • 原理:
    • 字符畫是一系列字符的組合,我們可以把字符看作是比較大塊的像 素,一個字符能表現一種顏色(爲了簡化可以這麼理解),字符的種 類越多,可以表現的顏色也越多,圖片也會更有層次感

8.1 名詞介紹

三原色

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-2yrJ6mgD-1573464810594)(image/1572937731508.png)]

顏色碼對照表

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-nstnJGD6-1573464810595)(image/1572937720073.png)]

灰度圖

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-0rM3PCRG-1573464810597)(image/1572937706912.png)]

點陣字

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-xqwEiJVj-1573464810598)(image/1572937687717.png)]

分辨率

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Psgzxe8l-1573464810599)(image/1572937672300.png)]

灰度圖

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-ajrpbVFF-1573464810600)(image/1572937659310.png)]

彩色圖

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-deRnnlQE-1573464810601)(image/1572937645119.png)]

8.2 原理步驟

  • 首先使用 PIL 的 Image.open 打開圖片文件,獲得對象 im
  • 使用 PIL 庫的 im.resize() 調整圖片大小對應到輸出的字符畫的寬 度和高度,注意這個函數第二個參數使用 Image.NEAREST,表示輸出低質量的圖片。
  • 遍歷提取圖片中每行的像素的 RGB 值,調用 getchar 轉成對應的字符
  • 將所有的像素對應的字符拼接在一起成爲一個字符串 txt
  • 打印輸出字符串 txt
  • 如果執行時配置了輸出文件,將打開文件將 txt 輸出到文件,如果 沒有,則默認輸出到 output.txt 文件

8.3 第三方模塊的引用

  • pip3 install pillow
  • pip3 install pyprind
  • pip3 install numpy
  • pip3 install opencv-python

8.4 視頻轉動畫實現設計思路

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-vNHP2H2a-1573464810602)(image/1572937402552.png)]

9 圖形用戶界面實戰【擴展】

9.1 圖形用戶界面(GUI)

  • 本節介紹如何創建Python程序的圖形用戶界面(GUI),也就是那些帶有按鈕和文本框的窗口。
  • 目前支持Python的所謂"GUI工具包"有很多,但沒有一個被認爲是標準的,也好,選擇空間大
  • GUI工具包:
工具包名 介紹 URL地址
Tkinter 使用Tk平臺。很容易得到。半標準 http://wiki.python.org/moin/TkInter
wxpython 基於wxWindows。跨平臺越來越流行 http://wxpython.org
PythonWin 只能在Windows上使用。 http://starship.python.net/crew/mhammond
Java Swing 只能用於Python。使用本機的Java GUI http://java.sun.com/docs/books/tutorial/uiswing
PyGTK 使用GTK平臺,在Linux上很流行 http://pygtk.org
PyQt 使用Qt平臺,跨平臺 http://wiki.python.org/moin/PyQt

9.2 安裝:wxpython

 pip install -U wxpython
 --Installing collected packages: six, wxpython
 --Successfully installed six-1.11.0 wxpython-4.0.1
  • 開發步驟:
1. 導入wx模塊
2. 定義一個應用程序對象
3. 創建wx.Frame主窗口對象,設置窗口標題和大小
4. 創建組件、佈局、添加事件處理等操作
5. 通過Frame的show()方法顯示窗體
6. 進入應用程序事件主循環

9.3 創建並且顯示一個框架

# 導入wxPython模塊
import wx
# 創建應用程序對象
app = wx.App()
win = wx.Frame(None)    #創建一個單獨的窗口
btn=wx.Button(win)  #創建一個按鈕組件
win.Show()          #設置可見
#進入應用程序事件主循環
app.MainLoop()

9.4 設置標題,添加按鈕

import wx
app = wx.App()
win = wx.Frame(None,title="我的記事本") #創建一個單獨的窗口
loadButton = wx.Button(win,label="Open")
saveButton = wx.Button(win,label="Save")
win.Show()
app.MainLoop()  #進入應用程序事件主循環

9.5 設置標題,添加按鈕,並簡單佈局

import wx
app = wx.App()
win = wx.Frame(None,title="我的記事本",size=(410,335)) #創建一個單獨的窗口

loadButton = wx.Button(win,label="Open",pos=(225,5),size=(80,25))
saveButton = wx.Button(win,label="Save",pos=(315,5),size=(80,25))

filename = wx.TextCtrl(win,pos=(5,5),size=(210,25))

contents = wx.TextCtrl(win,pos=(5,35),size=(390,260),style=wx.TE_MULTILINE | wx.HSCROLL)

win.Show()

app.MainLoop() #進入應用程序事件主循環

9.6 組件佈局

import wx
app = wx.App()
win = wx.Frame(None,title="我的記事本",size=(410,335)) #創建一個單獨的窗口
bkg = wx.Panel(win)

#創建組件
loadButton = wx.Button(bkg,label="Open")
saveButton = wx.Button(bkg,label="Save")
filename = wx.TextCtrl(bkg)
contents = wx.TextCtrl(bkg,style=wx.TE_MULTILINE | wx.HSCROLL)

#佈局容器
hbox=wx.BoxSizer() #默認水平佈局
hbox.Add(filename,proportion=1,flag=wx.EXPAND)
hbox.Add(loadButton,proportion=0,flag=wx.LEFT,border=5)
hbox.Add(saveButton,proportion=0,flag=wx.LEFT,border=5)

#佈局容器
vbox=wx.BoxSizer(wx.VERTICAL) #垂直佈局
vbox.Add(hbox,proportion=0,flag=wx.EXPAND|wx.ALL,border=5)
vbox.Add(contents,proportion=1,flag=wx.EXPAND|wx.LEFT|wx.BOTTOM|wx.RIGHT,border=50)

bkg.SetSizer(vbox)

win.Show()
app.MainLoop() #進入應用程序事件主循環

9.7 爲按鈕添加事件並完成其事件處理操作

import wx

#按鈕事件處理函數
def load(event):
    '''加載文件內容'''
    file=open(filename.GetValue(),"r")
    contents.SetValue(file.read())
    file.close()

def save(event):
    '''保持文件內容'''
    file=open(filename.GetValue(),"w")
    file.write(contents.GetValue())
    file.close()

#
app = wx.App()
win = wx.Frame(None,title="我的記事本",size=(410,335)) #創建一個單獨的窗口
win.Show()
loadButton = wx.Button(win,label="Open",pos=(225,5),size=(80,25))
saveButton = wx.Button(win,label="Save",pos=(315,5),size=(80,25))

loadButton.Bind(wx.EVT_BUTTON,load)
saveButton.Bind(wx.EVT_BUTTON,save)

filename = wx.TextCtrl(win,pos=(5,5),size=(210,25))

contents = wx.TextCtrl(win,pos=(5,35),size=(390,260),style=wx.TE_MULTILINE | wx.HSCROLL)

app.MainLoop() #進入應用程序事件主循環

10 飛機大戰【擴展】

  • 本次開發需要安裝一個Python的遊戲模塊:pygame。 方式:pip install pygame
  • 開發步驟如下:
    • 創建遊戲主頁面窗口,並添加滾動背景。
    • 添加鍵盤事件處理函數
    • 放置玩家英雄飛機,並綁定鍵盤事件,實現飛機移動
    • 添加玩家子彈,並實現發射
    • 隨機顯示敵機
    • 實現敵機與子彈的碰撞檢測
import pygame
from pygame.locals import *  #pygame使用的各種常量
import time,random

# 實現敵機與子彈的碰撞檢測。

class HeroPlane:
    ''' 玩家飛機類(英雄) '''
    def __init__(self, screen_temp):
        self.x = 200
        self.y = 400
        self.screen = screen_temp
        self.image = pygame.image.load("./images/me.png")
        self.bullet_list = [] #存儲發射出去的子彈對象引用

    def display(self):
        ''' 繪製玩家到窗口中 '''

        #遍歷移動子彈
        for bullet in self.bullet_list:
            bullet.display()
            #移動子彈,並判斷是否越界。
            if bullet.move():
                self.bullet_list.remove(bullet)

        self.screen.blit(self.image, (self.x, self.y))


    def move_left(self):
        ''' 左移動,並判斷防止越界 '''
        self.x -= 5
        if self.x<0:
            self.x=0

    def move_right(self):
        ''' 右移動,並判斷防止越界 '''
        self.x += 5
        if self.x > 406:
            self.x = 406
    def fire(self):
        self.bullet_list.append(Bullet(self.screen, self.x, self.y))
        print(len(self.bullet_list))

class Bullet:
    ''' 玩家子彈類 '''
    def __init__(self, screen_temp, x, y):
        self.x = x+51
        self.y = y
        self.screen = screen_temp
        self.image = pygame.image.load("./images/pd.png")

    def display(self):
        self.screen.blit(self.image, (self.x, self.y))

    def move(self):
        self.y-=10
        if self.y<-20:
            return True

class EnemyPlane:
    """敵機的類"""
    def __init__(self, screen_temp):
        self.x = random.choice(range(408))
        self.y = -75
        self.screen = screen_temp
        self.image = pygame.image.load("./images/e"+str(random.choice(range(3)))+".png")

    def display(self):
        self.screen.blit(self.image, (self.x, self.y))

    def move(self,hero):
        self.y += 4
        #遍歷玩家的子彈,並做碰撞檢測
        for bo in hero.bullet_list:
            if bo.x>self.x+12 and bo.x<self.x+92 and bo.y>self.y+20 and bo.y<self.y+60:
                hero.bullet_list.remove(bo)
                return True
        #判斷敵機是否越界
        if self.y>512:
            return True;

def key_control(hero_temp):
    ''' 鍵盤控制函數 '''

    #獲取事件,比如按鍵等
    for event in pygame.event.get():
        #判斷是否是點擊了退出按鈕
        if event.type == QUIT:
            print("exit")
            exit()

    #獲取按下的鍵(返回的是元組值)
    pressed_keys = pygame.key.get_pressed()
    #檢測是否按下a或者left鍵
    if pressed_keys[K_LEFT] or pressed_keys[K_a]:
        print('left')
        hero_temp.move_left()

    #檢測是否按下d或者right鍵
    elif pressed_keys[K_RIGHT] or pressed_keys[K_d]:
        print('right')
        hero_temp.move_right()

    #檢查是否是空格鍵
    if pressed_keys[K_SPACE]:
        print('space')
        hero_temp.fire()

def main():
    '''遊戲的主程序執行函數'''

    #1. 創建窗口:set_mode(分辨率=(0,0),標誌=0,深度=0)
    screen = pygame.display.set_mode((512,568),0,0)

    #2. 創建一個遊戲背景圖片(512*1536)
    background = pygame.image.load("./images/bg2.jpg")
    m=-968 #初始化遊戲背景圖片標軸y的值

    #3. 創建一個玩家飛機對象
    hero = HeroPlane(screen)

    #4.定義用於存放敵機列表
    enemylist = []

    while True:
        #繪製位圖
        screen.blit(background,(0,m))
        m+=2
        if m>=-200:
            m = -968

         #顯示英雄玩家
        hero.display()
        # 鍵盤控制(負責移動玩家)
        key_control(hero)

        #隨機輸出敵機
        if random.choice(range(50))==10:
            enemylist.append(EnemyPlane(screen))

        #遍歷所有敵機,顯示敵機,移動敵機,並與玩家子彈碰撞檢測
        for em in enemylist:
            em.display()
            if em.move(hero):
                enemylist.remove(em)

        #更新屏幕顯示
        pygame.display.update()

        # 定時睡眠(時鐘)
        time.sleep(0.04)

# 判斷當前是否是主程序,若是就執行主程序。
if __name__ == "__main__":
    main()

11 附錄:pip命令

------------------------------------------------------------
列出已安裝的包:
    $ pip list
    $ pip freeze     # 查看自己安裝的

安裝軟件(安裝特定版本的package,通過使用==, &gt;=, &lt;=, &gt;, &lt;來指定一個版本號)**
    $ pip install SomePackage
    $ pip install 'Markdown<2.0'
    $ pip install 'Markdown>2.0,<2.0.3'

卸載軟件pip uninstall SomePackage
    $ pip uninstall SomePackage

下載所需的軟件包:
    $ pip download SomePackage -d directory 
    例如下載PyMySQL軟件包
    $ pip download PyMySQL -d D:/pypackage

安裝下載好的軟件包文件
    $ pip install 目錄/軟件包文件名
    如安裝PyMySQL軟件包
    $ pip3.6 install D:/pypackage/PyMySQL-0.7.11-py2.py3-none-any.whl

##12 附錄: 類的專有方法(魔術方法)

類的專有方法:

  • __init__: 構造函數,在生成對象時調用

  • __del__: 析構函數,釋放對象時使用

  • __repr__: 打印,轉換

  • __setitem__ : 按照索引賦值

  • __getitem__: 按照索引獲取值

  • __len__: 獲得長度

  • __cmp__: 比較運算

  • __call__: 函數調用

  • __add__: 加運算

  • __sub__: 減運算

  • __mul__: 乘運算

  • __div__: 除運算

  • __mod__: 求餘運算

  • __pow__: 乘方

  • Python同樣支持運算符重寫,我們可以對類的專有方法進行重寫,實例如下:

#!/usr/bin/python3

class Vector:
   def __init__(self, a, b):
      self.a = a
      self.b = b

   def __str__(self):
      return 'Vector (%d, %d)' % (self.a, self.b)

   def __add__(self,other):
      return Vector(self.a + other.a, self.b + other.b)

v1 = Vector(2,10)
v2 = Vector(5,-2)
print (v1 + v2)

# 以上代碼執行結果如下所示:
# Vector(7,8)

發佈了5 篇原創文章 · 獲贊 162 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章