三、Python面向物件(二)
阿新 • • 發佈:2022-05-24
封裝
- 私有屬性
在屬性名前面加兩個連續的下劃線, 這個屬性就變成了私有屬性
私有屬性只能在類的內部使用, 不能在類的外部使用
''' 定義一個類, Person 有屬性 姓名name, 體重weight 有方法run, 呼叫一次run方法weight少0.5公斤 有方法eat, 呼叫一次eat方法weight多1公斤 有方法show, 作用就是介紹自己, 其實就是顯示name和weight的值 ''' class Person: def __init__(self, name, weight): self.name = name self.weight = weight def run(self): self.weight -= 0.5 def eat(self): self.weight += 1 def show(self): print(f"我的名字叫{self.name}, 我的體重為{self.weight}") xiaoming = Person("小明", 70) # 例項化小明物件, 體重是70公斤 xiaoming.show() xiaoming.run() xiaoming.run() xiaoming.run() xiaoming.show() xiaoming.eat() xiaoming.show() xiaomei = Person("小美", 50) xiaomei.show() xiaomei.eat() xiaomei.eat() xiaomei.eat() xiaomei.eat() xiaomei.eat() xiaomei.weight = 45 # 在類 的外部修改了屬性的值 xiaomei.show()
- 通過私有屬性封裝weight的案例
''' 定義一個類, Person 有屬性 姓名name, 體重weight 有方法run, 呼叫一次run方法weight少0.5公斤 有方法eat, 呼叫一次eat方法weight多1公斤 有方法show, 作用就是介紹自己, 其實就是顯示name和weight的值 ''' class Person: def __init__(self, name, weight): self.name = name self.__weight = weight # __weight屬性是私有的, 只能在類的內部使用 def run(self): self.__weight -= 0.5 def eat(self): self.__weight += 1 def show(self): print(f"我的名字叫{self.name}, 我的體重為{self.__weight}") xiaoming = Person("小明", 70) # 例項化小明物件, 體重是70公斤 xiaoming.show() xiaoming.run() xiaoming.run() xiaoming.run() xiaoming.show() xiaoming.eat() xiaoming.show() xiaomei = Person("小美", 50) xiaomei.show() xiaomei.eat() xiaomei.eat() xiaomei.eat() xiaomei.eat() xiaomei.eat() xiaomei.__weight = 45 # 在類 的外部修改了屬性的值, __weight是私有屬性, 在類的外部修改無效 xiaomei.show()
- 私有方法, 在方法名前面加兩個連續的下劃線, 方法就成為了私有方法
- 私有方法只能在類的內部呼叫, 不能在類的外部調動
class Person: def __secret(self): # __secret為私有方法, 只能在類的內部呼叫 print("我的祕密是, 睡覺磨牙打呼嚕") # 只有正確的輸入了密碼後, 才能看到小祕密 def show(self, password): if password == "123456": self.__secret() # 呼叫私有方法 else: print("密碼不對, 不告訴你我的小祕密哦") p = Person() # p.__secret() # 不能在類的外部呼叫私有方法__secret p.show(input("請輸入密碼"))
繼承
- 子類會繼承父類的方法和屬性
class 父類:
pass
class 子類(父類):
pass
class Animal: # 有一個動物類
def eat(self):
print("吃")
def sleep(self):
print("睡")
class Dog(Animal): # Dog是子類, 繼承自父類Animal
def run(self):
print("跑")
a = Animal()
a.sleep()
a.eat() # Animal類只有sleep和eat方法
d = Dog()
d.eat() # 繼承自父類
d.sleep() # 繼承自父類
d.run() # run是Dog自己的
# Dog類有eat, sleep和run三個方法
- 一個父類可以有多個子類
class Animal: # 有一個動物類
def eat(self):
print("吃")
def sleep(self):
print("睡")
class Dog(Animal): # Dog是子類, 繼承自父類Animal
def run(self):
print("跑")
class Fish(Animal):
def swimming(self):
print("游水")
class Bird(Animal):
def fly(self):
print("飛")
'''
Animal類有兩個方法, eat和sleep
Dog類有三個方法, eat和sleep還有run
Fish類有三個方法, eat和sleep還有swimming
Bird類有三個方法, eat和sleep還有fly
'''
a = Animal()
a.sleep()
a.eat()
d = Dog()
d.sleep()
d.eat()
d.run()
f = Fish()
f.sleep()
f.eat()
f.swimming()
b = Bird()
b.sleep()
b.eat()
b.fly()
- 多級繼承
class Animal: # 有一個動物類
def eat(self):
print("吃")
def sleep(self):
print("睡")
class Dog(Animal): # Dog是子類, 繼承自父類Animal
def run(self):
print("跑")
class ErHa(Dog):
def chaijia(self):
print("拆家")
class LangGou(Dog):
def yaoren(self):
print("咬人")
'''
類ErHa有eat, sleep, run和chaijia
類LangGou有有eat, sleep, run和yaoren
'''
e = ErHa()
e.sleep() # 來自於爺爺
e.eat() # 來自於爺爺
e.run() # 來自於父親
e.chaijia() # 自己定義的方法
l = LangGou()
l.sleep()
l.eat()
l.run()
l.yaoren()
Object類
- 在python3以上的版本中, 如果一個類沒有明確的指定父類, 這個類繼承自Object類
class A:
pass
# 類A繼承自Object類
# python所有的類, 都是Object的子類
方法的重寫
- 當父類的方法不能滿足子類的需求, 那麼子類就要修改方法
覆蓋
- 子類把父類的同名方法覆蓋
- 子類中如果出現和父類同名的方法, 那麼在子類中父類的同名方法就被覆蓋了
class Animal: # 有一個動物類
def eat(self):
print("吃")
def sleep(self):
print("睡")
class Dog(Animal): # Dog是子類, 繼承自父類Animal
def eat(self): # 如果子類出現了和父類同名的方法, 那麼在子類中, 父類的方法就不存在了
print("吃肉")
a = Animal()
a.eat() # Animal的eat沒有任何改變
d = Dog()
d.sleep() # 這個是來自於父類Animal
d.eat() # 這個是來自於自己
擴充套件super()
- 子類在父類方法上新增功能
- super().父類的同名方法
class Animal: # 有一個動物類
def eat(self):
print("吃")
class Dog(Animal): # Dog是子類, 繼承自父類Animal
def eat(self): # 如果子類出現了和父類同名的方法, 那麼在子類中, 父類的方法就不存在了
super().eat() # 呼叫父類的eat方法
print("dog類自己的eat方法")
d = Dog()
d.eat()
屬性的繼承
- 父類的屬性會繼承給子類
class Animal: # 有一個動物類
def __init__(self, sex, age):
self.sex = sex
self.age = age
class Dog(Animal): # 由於父類Animal定義了__init__, 所以子類會自動繼承這個父類的__init__
def show(self): # 顯示屬性sex和age
print(f"sex屬性的值為{self.sex}, age屬性的值為{self.age}")
# d = Dog() # 例項化的時候, 不提供__init__的實參, 語法會報錯
d = Dog("男", 10)
d.show()
# 在Dog中由於自動繼承了父類的__init__方法, 所以父類中定義的屬性sex和age也繼承給了子類
- 父類和子類都有自己的屬性
class Animal: # 有一個動物類, 有屬性sex和age
def __init__(self, sex, age):
self.sex = sex
self.age = age
# 子類Dog不但要繼承父類的屬性, 還有自己特有的屬性name
class Dog(Animal): # 由於父類Animal定義了__init__, 所以子類會自動繼承這個父類的__init__
# Dog類有三個屬性, sex和age來自於Animal, name來自於自己
def __init__(self, sex, age, name): # 這裡的三個形參的作用是給三個屬性提供初值
self.name = name
# 由於Dog類也有__init__, 所以會把Animal類的__init__覆蓋
super().__init__(sex, age) # 呼叫父類的__init__
def show(self): # 顯示屬性sex和age
print(f"name屬性的值為{self.name}, sex屬性的值為{self.sex}, age屬性的值為{self.age}")
d = Dog("男", 10, "tom")
d.show()
'''
類Animal有屬性age
類Dog繼承自Animal,有屬性name
類Dog有方法show, 顯示屬性age和name
'''
class Animal:
def __init__(self, age):
self.age = age
class Dog(Animal):
def __init__(self, name, age): # 由於Dog有兩個屬性name和age, 所以__init__提供了兩個對應的形參
self.name = name
super().__init__(age)
def show(self):
print(f"我的名字叫{self.name}, 我的年齡是{self.age}")
d = Dog("小狗", 3)
d.show()
父類的私有成員子類不能繼承
- 父類中帶有連續兩個下劃線的方法和屬性都是私有的, 私有的成員不能繼承給子類
class Animal:
def __init__(self):
self.__name = "動物" # __name是私有屬性
def __my_func(self): # 私有方法
pass
class Dog(Animal):
pass
d = Dog()
# d.__my_func() # 語法報錯, 錯誤的原因是父類的方法是私有的, 不會繼承給Dog類
# print(d.__name) # 語法報錯, 原因是父類的私有屬性,不能繼承給子類
多型
- 不同的子類物件, 呼叫相同的父類方法, 產生不同的執行結果
類A有方法show
類B1和B2都是繼承自類A
B1的物件呼叫show方法
B2的物件呼叫show方法
兩個物件呼叫的都是父類的show方法, 但執行的結果不一樣
'''
class A:
def show(self):
print("show")
class B1(A):
pass
class B2(A):
pass
b1 = B1()
b1.show()
b2 = B2()
b2.show()
# 上面的程式碼不同的子類物件, 呼叫相同的父類方法, 執行結果也是一樣, 這個不是多型
'''
class A:
def show(self):
self.my_func()
def my_func(self):
print("我是a")
class B1(A):
def my_func(self):
print("我是b1")
class B2(A):
def my_func(self):
print("我是b2")
b1 = B1()
b1.show() # 內部程式碼一定是呼叫了B1 的my_func方法
b2 = B2()
b2.show() # 內部程式碼一定是呼叫了B2 的my_func方法
類屬性
- 不需要類的例項, 直接可以使用的屬性
- 普通屬性(例項屬性)一定要在方法內部定義, 定義的時候self.屬性名 = 值
- 類屬性定義在類的內部, 但是在方法的外部, 定義的時候不需要使用self
- 類屬性直接用類名.類屬性名 使用
# 類A的name是普通屬性, 所以一定要例項化為物件後, 通過物件使用
class A:
def __init__(self, name):
self.name = name
a = A("tom")
print(a.name) # 使用物件a的屬性name
# 普通屬性, 也叫例項屬性, 這個屬性必須通過類的例項來使用
# 類B的name是類屬性, 所以不需要例項, 直接可以通過類名使用
class B:
name = "tom" # 定義了一個類屬性name, 值為"tom"
def __init__(self):
pass
print(B.name) # 使用B類的類屬性name
類方法
- 普通方法也叫例項方法,, 只能通過例項呼叫的方法
- 不需要例項, 直接通過類名.類方法名就能呼叫的方法
class 類名:
@classmethod # 定義類方法上面必須寫
def 類方法(cls, 形參1, 形參2......): # 類方法的第一個形參必須是cls
pass
class A:
@classmethod
def my_func(cls):
print("我是類方法my_func")
A.my_func()
類方法的形參cls的作用
cls的作用一
- 在類方法中使用類屬性
class A:
age = 0 # 這裡定義的類屬性age
@classmethod
def show_age(cls):
# 在類方法內部顯示類屬性的值
print(cls.age) # 在類方法內部使用類屬性
@classmethod
def set_age(cls, age): # age是形參
# 在類方法內部修改類屬性的值
cls.age = age # 把形參age的值給類屬性age
A.set_age(200)
A.show_age() # 呼叫類方法
cls的作用二
- 在一個類方法中呼叫另一個類方法
class A:
@classmethod
def my_func1(cls):
print("my_func1")
@classmethod
def my_func2(cls):
# 在這個類方法中呼叫my_func1類方法
cls.my_func1()
A.my_func2()
- 類方法使用場景
- 建立一個只能有一個例項的類(單例)
class A:
__tmp = None # __tmp是當做類的A物件來使用, 但程式碼在這裡還不確定, 所以給個None
@classmethod
def create(cls): # 只要呼叫類方法create就會生成類A的例項, 不管呼叫多少次, 但例項只有一個
if cls.__tmp is None:
cls.__tmp = A() # 如果條件成立, 執行例項化的過程
return cls.__tmp
else:
return cls.__tmp
def __init__(self): # 為了證明這個類到底被例項化了多少次
print("類A被例項化了, 因為例項化的時候會自動呼叫__init__")
# 類屬性tmp的值是None, 所以if cls.tmp is None這個條件成立, 所以執行了例項化的語句cls.tmp = A()
a = A.create()
# 類屬性tmp的值已經是一個物件了, 不是None, if cls.tmp is None條件不成立, 沒有執行例項化的程式碼
b = A.create()
# 類屬性tmp的值已經是一個物件了, 不是None, if cls.tmp is None條件不成立, 沒有執行例項化的程式碼
c = A.create()
# 不管呼叫多少次create, 但cls.__tmp = A()語句只執行過一次
靜態方法
- 不需要例項, 直接可以通過類名.靜態方法名 呼叫的方法
class 類名:
@staticmethod
def 靜態方法名(形參1, 形參2........):
pass
class A:
@staticmethod
def help(): # 定義靜態方法help
print("我是靜態方法help")
A.help() # 呼叫靜態方法help
- 使用場景
- 如果一個專案, 有很多函式, 函式多了, 名字會重名, 可以把一些和類確實無關, 但有擔心重名的函式, 做為類的靜態方法使用
- 目的是避免因為函式重名帶來的衝突
class A:
@staticmethod
def my_func():
print("aaaaaaaaaaaaaaa")
class B:
@staticmethod
def my_func():
print("bbbbbbbbbbbbbbbbbb")
A.my_func() # 本意是想呼叫第一個my_func