6.類和物件

jason8826發表於2024-05-10
# __init__() 構造方法(或建構函式)
# 建構函式可以包含多個引數,但必須包含一個名為self的引數,且必須作為第一個引數
# 建構函式可以手動新增,如果不新增,python也會為類新增一個僅包含self引數的構造方法
class Demo:
    num1 = '11'
    num2 = '22'
    def __init__(self,str1):
        print("傳入的引數:",str1)
    def say(self,str2): # 無論是構造方法還是例項方法,都要求將self引數作為方法的第一個引數
        print(str2)
# 建立物件時,會自動呼叫__init__()構造方法,且self引數是特殊引數,python會自動傳給它值,而'12345'是傳給str1的值
demo = Demo('12345')
# 訪問物件的變數和方法
demo.say("呼叫物件的方法")
print(demo.num1, demo.num2)
# 動態新增類物件的變數
demo.num3 = '33'
print('動態新增變數', demo.num3)
# 動態刪除類物件的變數del demo1.num3
# 動態修改類物件的變數
demo.num1 = '44'
print('動態修改變數', demo.num1)
# 動態新增類物件的方法
def say1():
    print('動態新增方法')
demo.say1 = say1
demo.say1()

# 類屬性細分:類屬性、例項屬性、區域性變數
# 類變數(或類屬性):類體中,所有函式之外的變數
# 注:類變數建議用類名呼叫,不建議用物件名呼叫
# 例項變數(或例項屬性):類體中,所有函式內部,以"self.變數名"方式定義的變數
class Demo1:
    num1 = '類變數1'
    def __init__(self):
        self.num1 = '例項變數1'
    def say(self):
        self.num2 = '例項變數2'
demo1 = Demo1()# 自動呼叫建構函式
print('建構函式中的例項變數:',demo1.num1)
demo1.say()# 需要先呼叫函式,才能擁有例項變數
print('例項函式中的例項變數:',demo1.num2)
# 類變數和例項變數同名時,物件將無法呼叫類變數,它會首選例項變數
print('物件訪問同名的類變數和例項變數', demo1.num1)
print('類訪問同名的類變數和例項變數', Demo1.num1)# 例項變數只能透過物件名訪問,無法透過類名訪問
# 區域性變數:類體中,所有函式內部,以"變數名=變數值"方式定義的變數

# 類方法細分:例項方法、類方法、靜態方法(實際幾乎用不到類方法和靜態方法)
# 例項方法:通常情況下,在類中定義的方法預設都是例項方法,至少要包含一個self引數
# 類方法:函式前,需要用@classmethod修飾
# 第一個引數self更名為cls
# 類方法建議用類名呼叫,不推薦用物件名呼叫
class Demo2:
    def __init__(self):
        self.num1 = '例項變數1'
    @classmethod
    def say(cls):
        print('類呼叫類方法,列印cls變數', cls)
Demo2.say()
# 靜態方法:就是函式,唯一區別,靜態方法的作用域為類空間,函式的作用域為程式的全域性名稱空間
# 函式前需要用@staticmethod修飾,沒有self和cls這樣的引數
class Demo3:
    def __init__(self):
        self.num1 = '例項變數1'
    @staticmethod
    def say():
        print('呼叫靜態方法')
Demo3.say()

# 類呼叫例項方法
class Demo4:
    def __init__(self):
        self.num1 = '例項變數1'
    def say(self):
        print('類呼叫例項方法')
demo4 = Demo4()
Demo4.say(demo4)# 類呼叫例項方法時,必須手動傳入self引數(物件呼叫例項方法時,python會自動給self值)

# python描述符,以下三個方法組成了描述符協議,如果這些方法中的任何一個被定義在一個物件中,這個物件就是一個描述符
# __get__(self,instance,owner):在讀取屬性時將呼叫該方法
# __set__(self,instance,value):在設定屬性時將呼叫該方法
# __delete__(self,instance):對屬性呼叫del時將呼叫該方法
# self:Demo5的例項物件,即myClass的屬性x;instance:myClass的例項物件,即m;owner:myClass這個類,擁有所有這些東西;vlaue:對m.x的賦值,即下面例子中的20
class Demo5:
    def __init__(self, val = None, name = 'kbq'):
        self.val = val
        self.name = name
    def __get__(self, instance, owner):
        print("getting",self.name)
        return self.val
    def __set__(self, instance, value):
        print("setting",self.name)
        self.val = value
class myClass:
    x = Demo5(10,'lm') # 如果一個類的某個屬性有資料描述符,查詢這個屬性時,會呼叫描述符的__get__()方法;對這個熟悉賦值時,也會呼叫__set__()方法。
    y = 5
m = myClass()
print(m.x)
m.x = 20
print(m.x)
print(m.y)

# 既保護類的封裝特性,又讓開發者可以使用“物件.屬性”的方式操作操作類屬性
# 方法一、property()函式
# 格式:屬性名=property(fget=None, fset=None, fdel=None, doc=None)
# 在使用property()函式時,以上4個引數可以僅指定第1個、或者前2個、或者前3個,或者全部指定。
class Demo6:
    def __init__(self):
        self.__name = "類的例項化" # name屬性必須設定為私有屬性,即使用__name(前面有2個下劃線)
    def setname(self,n):
        self.__name = 3*n
    def getname(self):
        return self.__name
    def delname(self):
        self.__name = "xxx"
    aa = property(getname,setname,delname,"指明出處")
demo6 = Demo6()
print('例項化之後print', demo6.aa)
demo6.aa = "呼叫setname()"
print('呼叫setname之後print', demo6.aa)
del demo6.aa # 呼叫delname()
print('呼叫delname之後print', demo6.aa)
# 方法二、@proprety裝飾器
class Demo7:
    def __init__(self):
        self.__area = "類的例項化"
    @property # 將area()方法轉化成同名只讀屬性的getter方法
    def area(self):
        return self.__area
    @area.setter # 將area()方法轉化成同名只讀屬性的setter方法
    def area(self,value):
        self.__area = 3*value
    @area.deleter # 將area()方法轉化成同名只讀屬性的deleter方法
    def area(self):
        self.__area = "xxx"
demo7 = Demo7()
print('裝飾器getter之後print',demo7.area) # 透過 @property裝飾器,可以直接透過方法名來訪問方法,不需要在方法名後新增一對“()”小括號
demo7.area = 90
print('裝飾器setter之後print',demo7.area)
del demo7.area
print('裝飾器deleter之後print',demo7.area)

# 繼承,如果該類沒有顯式指定繼承自哪個類,則預設繼承 object 類
# 格式:class 類名(父類1,父類2,...)
class people:
    def say(self):
        print('父類people的self.name:',self.name)
class animal:
    def display(self):
        print('父類animal')
class Person(people,animal): # 實際使用的時候儘量不要使用多繼承
    pass
person = Person()
person.name = "輸入的名字"
person.say()
person.display()

# 父類方法重寫
# 針對父類方法在子類中不適用的情況,需要對方法進行重寫
class Bird:
    def isWing(self):
        print("鳥有翅膀")
    def fly(self):
        print("鳥會飛")
class Ostrich(Bird):
    def fly(self):
        print("鴕鳥不會飛")
ostrich = Ostrich()
ostrich.fly()
Bird.fly(ostrich) # 呼叫父類中的fly()

# super()函式,(多個父類時,呼叫的是第一個父類的方法)
# 在子類中定義新的構造方法,必須要在該方法中呼叫父類的構造方法(因為子類如果使用父類的方法時,涉及父類構造方法中的屬性時,未呼叫父類構造方法相當於沒有給父類構造方法中的屬性做初始化,則程式執行會報錯),且有以下兩種方法:
# 1、未繫結方法:People.__init__(self,name)
# 2、super()函式:super().__init__(name)
class People:
    def __init__(self,name):
        self.name = name
    def say(self):
        print("人,名字為:",self.name)
class Animal:
    def __init__(self,food):
        self.food = food
    def display(self):
        print("動物,吃:",self.food)
class Person(People, Animal):
    def __init__(self,name,food):
        super().__init__(name) # 呼叫People類的構造方法
        Animal.__init__(self,food) # 使用未繫結方法呼叫Animal類構造方法
        print('5465565454')
per = Person("張三", "熟食")
per.say()
per.display()

# __slots__屬性其實是一個元組,只能限制為例項物件動態新增屬性和方法,而無法限制動態地為類新增屬性和方法
class Demo8:
    __slots__ = ('l') # 意味著該類的例項物件僅限於動態新增'l'這個熟悉或方法
def l(self):
    print('__slots__為類物件動態新增屬性和方法l')
def p(self):
    print('__slots__為類物件動態新增屬性和方法p')
demo8 = Demo8()
demo8.l = l
demo8.l(demo8)
# demo8.p = p # 執行會報錯,只能新增l

# type
# 用法一:type(obj)檢視變數的型別
# 用法二:type(name,bases,dict)用來建立類
# name表示類的名稱
# bases表示一個元組,用來儲存該類的父類
# dict表示一個字典,用於表示類內定義的屬性或方法
def say(self):
    print("type建立類")
Demo9 = type('Demo9', (object,), {'say':say, 'name':"類中的變數name"}) # (object,)元組中只有一個元素時,最後的逗號不能省略
demo9 = Demo9()
demo9.say()
print(demo9.name)

# MetaClass元類:1、必須顯示繼承type類;2、類中需要定義並實現__new__()方法
# 定義元類
class FirtMetaClass(type):
    def __new__(cls,name,bases,attrs):
        attrs['say_'+name] = lambda self,value,saying=name:print(saying+','+value+'!')
        return type.__new__(cls,name,bases,attrs)
# 建立類
class Hello(object,metaclass=FirtMetaClass):
    pass
class Hi(object,metaclass=FirtMetaClass):
    pass
# 建立例項
hello = Hello()
hi = Hi()
# 呼叫例項
hello.say_Hello('kbq')
hi.say_Hi('ora')

# 多型,具有不同功能的函式可以使用相同的函式名,這樣就可以用一個函式名呼叫不同內容的函式
class Animal:
    def talk(self):
        pass
class Cat(Animal):
    def talk(self):
        print("喵喵喵")
class Dog(Animal):
    def talk(self):
        print("汪汪汪")
def func(obj):
    obj.talk()
cat = Cat()
dog = Dog()
func(cat)
func(dog)

# 列舉類
# 列舉類不能用來例項化物件,
from enum import Enum
class Color(Enum):
    red = 1
    green = 2
    blue = 3
print('呼叫列舉類red:',Color.red)
print('呼叫列舉類的name:',Color.green.name)
print('呼叫列舉類的value:',Color.green.value)
for color in Color:
    print("遍歷列舉類",color)
# Enum()函式可接受2個引數,第一個用於指定列舉類的類名,第二個引數用於指定列舉類中的多個成員
Num = Enum("Num",('one','two','three'))
print('用Enum建立列舉類:',Num.one.name,Num.one.value)

相關文章