1. 程式人生 > 其它 >三、Python面向物件(二)

三、Python面向物件(二)

面向物件程式設計三大特性

封裝

  • 私有屬性
在屬性名前面加兩個連續的下劃線, 這個屬性就變成了私有屬性
私有屬性只能在類的內部使用, 不能在類的外部使用
'''
定義一個類, 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

總結