類
- 我們目前所學習的對象都是Python內置的對象
- 但是內置對象并不能滿足所有的需求,所以我們在開發中經常需要自定義一些對象
- 類,簡單理解它就相當于一個圖紙。在程序中我們需要根據類來創建對象
- 類就是對象的圖紙!
- 我們也稱對象是類的實例(instance)
- 如果多個對象是通過一個類創建的,我們稱這些對象是一類對象
- 像 int() float() bool() str() list() dict() … 這些都是類
- a = int(10) # 創建一個int類的實例 等價于 a = 10
- 我們自定義的類都需要使用大寫字母開頭,使用大駝峰命名法(帕斯卡命名法)來對類命名
- 類也是一個對象!
- 類就是一個用來創建對象的對象!
- 類是type類型的對象,定義類實際上就是定義了一個type類型的對象
類的定義
嘗試定義一個表示人的類
class Person :# 在類的代碼塊中,我們可以定義變量和函數# 在類中我們所定義的變量,將會成為所有的實例的公共屬性# 所有實例都可以訪問這些變量name = 'swk' # 公共屬性,所有實例都可以訪問# 在類中也可以定義函數,類中的定義的函數,我們稱為方法# 這些方法可以通過該類的所有實例來訪問def say_hello(self) :# 方法每次被調用時,解析器都會自動傳遞第一個實參# 第一個參數,就是調用方法的對象本身,# 如果是p1調的,則第一個參數就是p1對象# 如果是p2調的,則第一個參數就是p2對象# 一般我們都會將這個參數命名為self# say_hello()這個方法,可以顯示如下格式的數據:# 你好!我是 xxx# 在方法中不能直接訪問類中的屬性print('你好!我是 %s' %self.name)
創建Person的實例
p2 = Person()print(p2.name)
- 調用方法,對象.方法名()
- 方法調用和函數調用的區別
- 如果是函數調用,則調用時傳幾個參數,就會有幾個實參
- 但是如果是方法調用,默認傳遞一個參數,所以方法中至少要定義一個形參
修改p1的name屬性
p1.name = '豬八戒'
p2.name = '沙和尚'p1.say_hello() # '你好!我是 豬八戒'
p2.say_hello() # '你好!我是 沙和尚'
del p2.name # 刪除p2的name屬性print(p1.name)
print(p2.name)
class Dog:'''表示狗的類'''def __init__(self , name , age , gender , height):self.name = nameself.age = ageself.gender = genderself.height = heightdef jiao(self):'''狗叫的方法'''print('汪汪汪~~~')def yao(self):'''狗咬的方法''' print('我咬你~~')def run(self):print('%s 快樂的奔跑著~~'%self.name) d = Dog('小黑',8,'male',30)# 目前我們可以直接通過 對象.屬性 的方式來修改屬性的值,這種方式導致對象中的屬性可以隨意修改
# 非常的不安全,值可以任意修改,不論對錯
# 現在我們就需要一種方式來增強數據的安全性
# 1.屬性不能隨意修改(我讓你改你才能改,不讓你改你就不能改)
# 2.屬性不能修改為任意的值(年齡不能是負數)
d.name = '阿黃'
d.age = -10
d.run()print(d.age)
封裝
封裝是面向對象的三大特性之一
封裝指的是隱藏對象中一些不希望被外部所訪問到的屬性或方法
如何隱藏一個對象中的屬性?
- 將對象的屬性名,修改為一個外部不知道的名字
如何獲取(修改)對象中的屬性? - 需要提供一個getter和setter方法使外部可以訪問到屬性
- getter 獲取對象中的指定屬性(get_屬性名)
- setter 用來設置對象的指定屬性(set_屬性名)
使用封裝,確實增加了類的定義的復雜程度,但是它也確保了數據的安全性 - 1.隱藏了屬性名,使調用者無法隨意的修改對象中的屬性
- 2.增加了getter和setter方法,很好的控制的屬性是否是只讀的
如果希望屬性是只讀的,則可以直接去掉setter方法
如果希望屬性不能被外部訪問,則可以直接去掉getter方法 - 3.使用setter方法設置屬性,可以增加數據的驗證,確保數據的值是正確的
- 4.使用getter方法獲取屬性,使用setter方法設置屬性
可以在讀取屬性和修改屬性的同時做一些其他的處理 - 5.使用getter方法可以表示一些計算的屬性
目前我們可以直接通過 對象.屬性 的方式來修改屬性的值,這種方式導致對象中的屬性可以隨意修改
- 非常的不安全,值可以任意修改,不論對錯
- 現在我們就需要一種方式來增強數據的安全性
- 1.屬性不能隨意修改(我讓你改你才能改,不讓你改你就不能改)
- 2.屬性不能修改為任意的值(年齡不能是負數)
class Dog:'''表示狗的類'''def __init__(self , name , age):self.hidden_name = nameself.hidden_age = agedef say_hello(self):print('大家好,我是 %s'%self.hidden_name) def get_name(self):'''get_name()用來獲取對象的name屬性''' # print('用戶讀取了屬性')return self.hidden_namedef set_name(self , name):# print('用戶修改了屬性')self.hidden_name = namedef get_age(self):return self.hidden_agedef set_age(self , age):if age > 0 :self.hidden_age = age
d = Dog('旺財',8)
print(d.get_age())
d = Dog('旺財',8)# 調用setter來修改name屬性
d.set_name('小黑')
d.set_age(-10)d.say_hello()
print(d.get_age())
property裝飾器
- property裝飾器,用來將一個get方法,轉換為對象的屬性
- 添加為property裝飾器以后,我們就可以像調用屬性一樣使用get方法
- 使用property裝飾的方法,必須和屬性名是一樣的
class Person:def __init__(self,name,age):self._name = nameself._age = age@property def name(self):print('get方法執行了~~~')return self._name# setter方法的裝飾器:@屬性名.setter@name.setter def name(self , name):print('setter方法調用了')self._name = name @propertydef age(self):return self._age@age.setter def age(self , age):self._age = age p = Person('豬八戒',18)p.name = '孫悟空'
p.age = 28print(p.name,p.age)
- 使用__開頭的屬性,實際上依然可以在外部訪問,所以這種方式我們一般不用
- 一般我們會將一些私有屬性(不希望被外部訪問的屬性)以_開頭
- 一般情況下,使用_開頭的屬性都是私有屬性,沒有特殊需要不要修改私有屬性
class Person:def __init__(self, name, age):self.__name = name # 私有屬性,調用會報錯self.age = age # 公開屬性,調用不會報錯def get_name(self):return self.__namedef get_age(self):return self.agedef set_name(self, name):self.__name = namep = Person('孫悟空', 12)
print(p.age)
print(p.__name)
繼承
- 有一個類,能夠實現我們需要的大部分功能,但是不能實現全部功能
- 如何能讓這個類來實現全部的功能呢?
- ① 直接修改這個類,在這個類中添加我們需要的功能
修改起來會比較麻煩,并且會違反OCP原則 - ② 直接創建一個新的類
創建一個新的類比較麻煩,并且需要大量的進行復制粘貼,會出現大量的重復性代碼 - ③ 直接從Animal類中來繼承它的屬性和方法
繼承是面向對象三大特性之一
通過繼承我們可以使一個類獲取到其他類中的屬性和方法
在定義類時,可以在類名后的括號中指定當前類的父類(超類、基類、super)
子類(衍生類)可以直接繼承父類中的所有的屬性和方法 - 通過繼承可以直接讓子類獲取到父類的方法或屬性,避免編寫重復性的代碼,并且也符合OCP原則
- 把要繼承的類當作參數傳入傳入一個類,這個類就繼承了參數類
# 定義一個類 Animal(動物)
# 這個類中需要兩個方法:run() sleep()
class Animal:def run(self):print('動物會跑~~~')def sleep(self):print('動物睡覺~~~')# todo 把要繼承的類當作參數傳入傳入一個類,這個類就繼承了參數類class Dog(Animal):def bark(self):print('汪汪汪~~~')def run(self):print('狗跑~~~~')class Hashiqi(Dog):def fan_sha(self):print('我是一只傻傻的哈士奇')# todo 實例化 Dog類
d = Dog()
print("Dog類的方法")
d.bark()
print("Dog類繼承的Animal的方法")
d.sleep()hsq = Hashiqi()
print("Hashiqi類繼承的Animal的方法")
hsq.fan_sha()
print("Hashiqi類繼承的Dog類,Dog類繼承了Animal的方法,所以Hashiqi可以使用Animal的方法,Hashiqi相當于間接的繼承了Animal")
hsq.sleep()
print("Hashiqi類繼承的Dog的方法")
hsq.bark()
重寫
如果在子類中如果有和父類同名的方法,則通過子類實例去調用方法時,
會調用子類的方法而不是父類的方法,這個特點我們成為叫做方法的重寫(覆蓋,override)
- 當我們調用一個對象的方法時
- 會優先去當前對象中尋找是否具有該方法,如果有則直接調用
- 如果沒有,則去當前對象的父類中尋找,如果父類中有則直接調用父類中的方法,
- 如果沒有,則去父類的父類中尋找,以此類推,直到找到object,如果依然沒有找到,則報錯
class A(object):def test(self):print('AAA')class B(A):def test(self):print('BBB')class C(B):def test(self):print('CCC') # 創建一個c的實例
c = C()
c.test()
類中的屬性和方法
# 定義一個類
class A(object):# 類屬性# 實例屬性# 類方法# 實例方法# 靜態方法# 類屬性,直接在類中定義的屬性是類屬性# 類屬性可以通過類或類的實例訪問到# 但是類屬性只能通過類對象來修改,無法通過實例對象修改count = 0def __init__(self):# 實例屬性,通過實例對象添加的屬性屬于實例屬性# 實例屬性只能通過實例對象來訪問和修改,類對象無法訪問修改self.name = '孫悟空'# 實例方法# 在類中定義,以self為第一個參數的方法都是實例方法# 實例方法在調用時,Python會將調用對象作為self傳入 # 實例方法可以通過實例和類去調用# 當通過實例調用時,會自動將當前調用對象作為self傳入# 當通過類調用時,不會自動傳遞self,此時我們必須手動傳遞selfdef test(self):print('這是test方法~~~ ', self)# 類方法# 在類內部使用 @classmethod 來修飾的方法屬于類方法# 類方法的第一個參數是cls,也會被自動傳遞,cls就是當前的類對象# 類方法和實例方法的區別,實例方法的第一個參數是self,而類方法的第一個參數是cls# 類方法可以通過類去調用,也可以通過實例調用,沒有區別@classmethoddef test_2(cls):print('這是test_2方法,他是一個類方法~~~ ', cls)print(cls.count)# 靜態方法# 在類中使用 @staticmethod 來修飾的方法屬于靜態方法 # 靜態方法不需要指定任何的默認參數,靜態方法可以通過類和實例去調用 # 靜態方法,基本上是一個和當前類無關的方法,它只是一個保存到當前類中的函數# 靜態方法一般都是一些工具方法,和當前類無關@staticmethoddef test_3():print('test_3執行了~~~')a = A()
# 實例屬性,通過實例對象添加的屬性屬于實例屬性
a.count = 10
A.count = 100
print('A ,', A.count)
print('a ,', a.count)
print('a ,', a.name)
A.test_3()
a.test_3()
print("實例屬性只能通過實例對象來訪問和修改,類對象無法訪問修改")
print('A ,', A.name)
垃圾回收
- 在Python中有自動的垃圾回收機制,它會自動將這些沒有被引用的對象刪除
- 所以我們不用手動處理垃圾回收
# 就像我們生活中會產生垃圾一樣,程序在運行過程當中也會產生垃圾
# 程序運行過程中產生的垃圾會影響到程序的運行的運行性能,所以這些垃圾必須被及時清理
# 沒用的東西就是垃圾
# 在程序中沒有被引用的對象就是垃圾,這種垃圾對象過多以后會影響到程序的運行的性能
# 所以我們必須進行及時的垃圾回收,所謂的垃圾回收就是講垃圾對象從內存中刪除
# 在Python中有自動的垃圾回收機制,它會自動將這些沒有被引用的對象刪除,
# 所以我們不用手動處理垃圾回收class A:def __init__(self):self.name = 'A類'# del是一個特殊方法,它會在對象被垃圾回收前調用def __del__(self):print('A()對象被刪除了~~~',self)a = A()
b = a # 又使用一個變量b,來引用a對應的對象print(a.name)# a = None # 將a設置為了None,此時沒有任何的變量對A()對象進行引用,它就是變成了垃圾
# b = None
# del a
# del b
input('回車鍵退出...')
模塊(module)
模塊化,模塊化指將一個完整的程序分解為一個一個小的模塊
- 通過將模塊組合,來搭建出一個完整的程序
- 不采用模塊化,統一將所有的代碼編寫到一個文件中
- 采用模塊化,將程序分別編寫到多個文件中
模塊化的特點:
- ① 方便開發
- ② 方便維護
- ③ 模塊可以復用!
在一個模塊中引入外部模塊
① import 模塊名 (模塊名,就是python文件的名字,注意不要py)
② import 模塊名 as 模塊別名
- 可以引入同一個模塊多次,但是模塊的實例只會創建一個
- import可以在程序的任意位置調用,但是一般情況下,import語句都會統一寫在程序的開頭
- 在每一個模塊內部都有一個__name__屬性,通過這個屬性可以獲取到模塊的名字
- __name__屬性值為 __main__的模塊是主模塊,一個程序中只會有一個主模塊
主模塊就是我們直接通過 python 執行的模塊
包 (Package)
- 包也是一個模塊
- 當我們模塊中代碼過多時,或者一個模塊需要被分解為多個模塊時,這時就需要使用到包
- 普通的模塊就是一個py文件,而包是一個文件夾
- 包中必須要一個一個 init.py 這個文件,這個文件中可以包含有包中的主要內容
標準庫
- 為了實現開箱即用的思想,Python中為我們提供了一個模塊的標準庫
- 在這個標準庫中,有很多很強大的模塊我們可以直接使用,
- 并且標準庫會隨Python的安裝一同安裝
- sys模塊,它里面提供了一些變量和函數,使我們可以獲取到Python解析器的信息
- 或者通過函數來操作Python解析器
# 引入sys模塊
import sys
# sys.argv
# 獲取執行代碼時,命令行中所包含的參數
# 該屬性是一個列表,列表中保存了當前命令的所有參數
print(sys.argv)