python類和對象理解


類的概念

類(class)抽象的概念,比如說人類、鳥類、水果、是一個總的稱呼,沒有具體到某個物體;

對象(object,指具體實例,instance);

類定義的語法:

class 類名:
  執行語句
  類變量
  類方法

 類最重要的兩部分就是類變量和類方法,類成員之間的可以相互調用。

程序可以在類中給新變量賦值就是增加類變量,可以通過del語句刪除已有類的變量。

在__init__構造函數(構造函數后面會說到)里面的是實例變量,程序可以任何位置(類里面或者類外面)增加實例變量,刪除則用del語句。

在實例方法中有一個特別的方法 :__init__ ,這個方法被稱為構造方法 。 構造方法用於構造該類的對象, Python 通過調用構造方法返回該類的對象 。 python提供一個功能就是:若開發者沒有定義構造函數,python會自動提供一個只包含self參數默認的構造方法。

class Bird:
    '這是學習python的第一個類'
    eyes = "two"
    def __init__(self, color,feet):
        '為python對象增                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 '
        self.color = color
        self.feet = feet
    def call(self,cd):
        print("This bird:",cd)

上面的 Bird類定義了 個構造方法,該構造方法只是方法名比較特殊:__init__ ,該方法的第一個參數同樣是 self,被綁定到構造方法初始化的對象 。和函數定義文檔類似,類文檔用字符串即可。該文檔同樣被放在類聲明之后、類體之前。

下面是調用類的構造方法創建對象。第二行是調用實例方法。

xique = Bird("green","two")
xique.call("gezhi")
#打印實例變量
print(xique.color,xique.feet)
#訪問實例變量,對實例變量賦值
xique.color = "brown"
#增加實例變量
xique.skin = 'yellow'
#打印編輯后的實例變量
print(xique.color,xique.feet,xique.skin)
#調用實例方法call(),第一個參數仍是self,self代表實例本身,
#是自動綁定的,不需要再次輸入,因此調用實例方法,只需再輸入一個參數即可
xique.call("zhizhizhizhi")

同一個類的多個對象具有相同的特征,類用定義多個對象的相同特征。類不是一個具體的實體,對象才是一個具體的實體

class Person:
    '這是學習python的第二個類'
    hair = 'black'
    def __init__(self,name = "Will",age = 8):
        '下面對Person對象增加兩個實例變量'
        self.name = name
        self.age = age
    '定義一個say()方法'
    def say(self,content):
        print(content)

給對象增加一個實例變量

# 增加一個skills實例變量
p.skills = ['programming','writing']
print(p.skills)
#刪除p對象的name實例變量,而不是刪除類中變量,新建一個對象,name實例變量還是構造函數默認的。
del p.name
# print(p.name) 會報錯

給對象增加類方法

 1 #動態增加方法
 2 def info(self):
 3     print("-----info函數-----",self)
 4     print("動態增加方法")
 5     return None
 6 p.foo = info  # foo實例方法名,info是我們在外面定義的方法,當然二者名字可以相同
 7 
 8 '''方法增加,只用了變量名,后面並沒有加括號方法動態增加,第一個參數並沒有綁定給調
 9 用類的對象,所以我們需要手動把第一個參數輸入,第一個參數是實例本身,可用實例名代表'''
10 print(p.foo(p))
11 
12 #如果想自動綁定,調用方法時,不想輸入self參數,可以用type模塊包裝
13 #重新寫一個函數
14 def message(self,comment):
15     print("我的個人信息是 %s " % comment)
16     return None
17 
18 p2 = Person()
19 p2.message = message
20 
21 from types import MethodType
22 p2.message = MethodType(p2.message,per) 
23 
24 #綜上,整隊某個對象動態增加的類變量和類方法,只能應用此對象,其他對象需重新增加

 實例方法調用另一個實例方法

 1 class Dog:
 2     skin = "white"
 3     def __init__(self,name = 'Aled'):
 4         self.name = name
 5     def jump(self):
 6         print("執行jump方法")
 7     def run(self):
 8         self.jump()  #此處不能寫成jump()必須有self,通過調用實例對象的方法
 9         print("調用run方法")
10 
11 g = Dog()
12 g.run() #通過run()方法,調用了jump()方法

類中self參數就是實例本身,可以自動綁定。

 1 #在構造方法中,self參數(第一個參數)表示該構造函數正在初始化的對象
 2 class InConstructor:
 3     def __init__(self):
 4         #在構造方法中定義一個foo變量(局部變量),臨時的
 5         foo = 1 #這是一個局部變量,不是實例變量,外界無法訪問
 6         print(type(foo))
 7         print(foo)
 8         #把構造方法正在初始化的foo變量編程實例變量,並且重新復制
 9         self.foo = 5
10 p = InConstructor()
11 print(p.foo)
12 
13 #自動綁定的self參數不依賴具體的調用方式,不管是以方法調用還是函數調用,self參數用一樣可以自動綁定
14 class User:
15     def test(self):
16         print("self參數:",self)
17 u = User()
18 u.test() #self參數: <__main__.User object at 0x013ADCF0> User對象在內存...
19 
20 #將User對象的test方法賦值給foo變量
21 foo = u.test #只需將名字賦值,不要加括號
22 #通過foo變量調用test()方法
23 foo() #效果 一樣,因為foo也是指向u.test
24 #self參數可以作為實例方法返回值

 self可以作為變量來訪問,或者作為實例方法的返回值

當 self 參數作為對象的默認引用時,程序可以像訪問普通變量一樣來訪 問這個self 參數,甚至可以把 self 參數當成實例方法的返回值 。 看下面程序 。 

 1 class ReturnSelf:
 2     def grow(self):
 3         if hasattr(self,'age'):
 4             self.age += 1
 5             print("有age變量")
 6         else:
 7             self.age = 22
 8             print("無age變量")
 9         return self #返回調用該方法的實例對象
10     def isnotexist(self):
11         '實例變量不一定非要在構造方法中定義,也可以在類外,或者類里的實例方法中定義'
12         print(self.age)
13 
14 rs = ReturnSelf()
15 print(rs.grow().age) # 22
16 rs.isnotexist() #22
17 rs.grow().grow().isnotexist() #返回值是self實例本身,然后可以多次調用實例方法或變量

 類也能調用實例方法

 類名.method(參數)

類名.變量名

 1 #類調用實例方法 類名.method
 2 
 3 #前面都是通過創建對象,通過對象調用實例方法
 4 #類很大程度上類似命名空間和定義變量和定義函數沒有什么不同
 5 
 6 #定義全局空間的foo函數
 7 def foo():
 8     print("全局空間的foo方法")
 9 #定義全局空間bar變量
10 bar = 20 
11 class Bird:
12     #定義Bird空間的foo函數
13     def foo(self):
14         print("Bird空間的foo方法")
15     #定義Bird空間的bar變量
16     bar = 200
17 #調用全局空間的函數和變量
18 foo()
19 print(bar)
20 #調用Bird空間的函數和變量
21 # Bird.foo() 這樣會報錯。缺少self參數
22 print(Bird.bar)

直接類名+方法會報錯,方法里面必須手動添加參數

1 class User:
2     def walk(self):
3         print(self,'正在慢慢走')
4 #通過類調用實例方法
5 # User.walk() 這樣報錯 TypeError

u = User() 

User.walk(u)  #顯式方法的第一個參數綁定參數值

這樣的調用效果等同於u.walk()

實際上,當通過 User 類調用 walk()實例方法時, Python只要求手動為第一個參數綁定參數值,並不要求必須綁定 Use對象,因此也可使用如下代碼進行調用 。 

1 User.walk("任意輸入都有可以,不一定非得是self參數")

如果傳入兩個參數呢

1 class Peaple:
2     def walk(self,name = "Will"):
3         print(name,"坐着",self)
4 #和普通函數調用沒啥區別,就當self是一個變量名,參數隨便輸入
5 Peaple.walk("吃飯")
6 Peaple.walk("吃飯","Alex")

類方法和靜態方法

 Python其實可以支持類方法定義,區別前面的實例方法,同時只是靜態方法定義,類方法和靜態方法類似,都可以通過類調用(同時也支持對象調用)區別在於類方法第一個參數為cls,會自動綁定到類,而靜態方法不會自動綁定到類

class Bird:
    #使用classmethod是類方法
    @classmethod
    def fly(cls):
        print('類方法fly:',cls)
    #使用staticmethod修飾的是靜態方法
    @staticmethod
    def info(p):
        print('靜態方法info:',p)
#調用類方法,類會自動綁定到第一個參數cls
Bird.fly()
#調用靜態方法,不會自動綁定,意思是第一個參數必須手動輸入
Bird.info("真麻煩")
#創建Bird對象
b = Bird()
#使用對象調用fly類方法,其實還是使用類調用
#因此第一個參數 依然自動綁定到Bird類
b.fly()
#使用對象調用info靜態方法,其實還是使用類調用
b.info('fkit')

 裝飾器

 1 #裝飾器
 2 #funA裝飾funB,funB作為參數引入到funA中,同時funA返回值就是修飾后的返回值
 3 def funA(funB):
 4     print("A")
 5     funB()
 6     return "最終修飾結果"
 7 def funB():
 8     print("B")
 9 funB()
10 
11 @funA
12 def funB():
13     print("B")
14 print(type(funB)) #這里是函數名,不是函數調用
15 print(funB) #裝飾函數修飾后,通過被修飾函數名查看返回值

這個函數裝飾器導致被修飾的函數變成了字符串,那么函數裝飾器有什么用?別忘記了,被修飾的函數總是被替換成@符號所引用的函數的返回值,因此被修飾的函數會變成什么,完全由於@符號所引用的函數的返回值決定一一如果@符號所引用的函數的返回值是函數,那么被修飾的函數在替換之后還是函數 。

 1 def foo(fn):
 2     #定義一個嵌套函數
 3     def bar(*args):
 4         print("===1===",args)
 5         n = args[0]
 6         print("===2===",n * (n -1))
 7         #查看傳遞給foo函數的fn函數
 8         print(fn.__name__)
 9         fn(n * (n -1))
10         print("*" * 15)
11         return fn(n * (n -1))
12     return bar
13 
14 @foo
15 def my_test(a):
16     print("===my_test函數===",a)
17 print(my_test)  #返回值是bar函數<function foo.<locals>.bar at 0x032C3D20>
18 my_test(10)  #意思就是my_test函數被bar函數替換,調用my_test函數就是調用bar函數
19 my_test(6,5)

上面裝飾結果相當於foo(my_test),my_test函數會被替換(裝飾)成foo(my_test)的返回值,他的返回值是bar函數,因此funB(my_test函數)就是bar函數

 通過修飾函數,也可以在修飾函數中添加權限檢查,邏輯驗證,異常處理

下面將示范通過函數修飾器給函數添加權限檢查的功能:

 1 def auth(fn):
 2     def auth_fn(*args):
 3         print("----模擬執行權限檢查-----")
 4         #回調被修飾的目標函數
 5         fn(*args) #作為函數參數時前面必須有*,如果在函數里面的參數則為args
 6     return auth_fn
 7 @auth
 8 def bedecorated(a,b):
 9     print("執行bedecotated函數,參數a:%s,參數b:%s" % (a, b))
10 
11 #調用bedecorated函數,其實就是調用修飾后返回的auth_fn函數
12 
13 bedecorated(4,5)    

類命名空間

1 #類命名空間
2 global_fn = lambda p: print("執行lambda表達式,P參數:",p)
3 class Category:
4     cate_fn = lambda p:print("執行lambda表達式,p參數",p)
5 #調用全局的global_fn,為參數p傳入參數值
6 global_fn('fkit')
7 c = Category()
8 c.cate_fn()

綜上,在全局命名空間調用,類命名空間的lambda函數也可以調用,通過類對象調用lambda函數相當於調用類方法,python同樣會自動為該剛方法綁定第一個參數值(self),也就是實例對象本身

成員變量

 1 class Address:
 2     detail = '廣州'
 3     post_code = '2019723'
 4     def info(self):
 5         #嘗試直接訪問類變量
 6         #print(detail) 報錯
 7         print(Address.detail)
 8         print(Address.post_code)
 9 Address.info(32) #通過類調用方法,需要手動輸入第一個參數
10 #通過類來訪問Address類的類變量
11 print(Address.detail)
12 addr = Address()
13 addr.info()
14 #修改Address的類變量
15 Address.detail = "佛山"
16 Address.post_code = '2018723'
17 addr.info()

 在類命名空間內定義的變量就屬於類變量 , Python 可以使用類來讀取、修改類變量。 對於類變量而言,它們就是屬於在類命名空間內定義的變量 ,因此程序不能直接訪問這些變量,程序必須使用類名來調用類變量。不管是在全局范圍內還是函數內訪問這些類變量,都必須使用類名進行訪問。

 1 #python也可以使用對象訪問類變量,建議使用類訪問類變量
 2 class Record:
 3     #定義兩個類變量
 4     item = '鼠標'
 5     date = '2019-07-23'
 6     def info(self):
 7         print('info方法:',self.item)
 8         print('info方法:',self.date)
 9 rc = Record()
10 print(rc.item)
11 print(rc.date)
12 rc.info()
13 
14 #修改Record類的兩個類變量
15 Record.item = "鍵盤"
16 Record.date = '2020-01-01'
17 rc.info()

 

 Python 允許通過對象訪問類變量 ,但如果程序通過對象嘗試對類變量賦值,此時性質就變了一Python 是動態語言,賦值語句往往意味着定義新變量。因此,如果程序通過對象對類變量賦值,其實不是對“類變量賦值”,而是定義新的實例變量 。例如如下程序 。

 1 class Inventory:
 2     #定義兩個變量
 3     quantity = 2000
 4     item = '鼠標'
 5     #定義實例方法
 6     def change(self,item,quantity):
 7         self.item = item
 8         self.quantity = quantity
 9 #創建Inventory對象
10 iv = Inventory()
11 iv.change('顯示器',500)
12 #訪問iv的item和quantity實例變量
13 print(iv.item) #顯示器
14 print(iv.quantity) #500
15 
16 #訪問Inventotry的item和quantity類變量
17 print(Inventory.item) #鼠標
18 print(Inventory.quantity) #2000

 

通過類修改類變量的值,實例變量不會受到影響

1 Inventory.item = '類變量item'
2 Inventory.quantity = '類變量quantity'
3 #訪問iv對象的實例變量item和quantity
4 print(iv.item) #顯示器
5 print(iv.quantity) #500

同樣修改實例變量的值,這種修改也不影響類變量或者其他對象的實例變量。

1 iv.item = 'iv實例變量item'
2 iv.quantity = 'iv實例變量quantity'
3 print(Inventory.item)
4 print(Inventory.quantity)
5 iv2 = Inventory()
6 print(iv2.item) #類變量item,前面修改了類變量,此處沒有調用change方法創建新的實例變量,所以這樣調用的是類變量
7 #新對象創建實例變量item ,quantity
8 iv2.change('音響',600)
9 print(iv2.item) #音響,這次就不是類變量了,因為創建的實例變量,python內在機制,實例可以調用類變量,其實還是通過類訪問的類變量

 使用property函數定義屬性

 1 class Retangle:
 2     def __init__(self,width,height):
 3         self.width = width
 4         self.height = height
 5     def setsize(self,size_):
 6         self.width,self.height = size_
 7     def getsize(self):
 8         return self.width,self.height
 9     def delsize(self):
10         self.width,self.height = 0,0
11     size = property(getsize,setsize,delsize,'描述事物的屬性')
12 #訪問size的說明文檔
13 print(Retangle.size.__doc__)
14 #通過內置的help函數查看說明文檔
15 help(Retangle.size)
16 rect = Retangle(4,3)
17 print(rect.size)
18 rect.setsize((7,9))
19 print(rect.size)
20 rect.size = 9,7
21 print(rect.width,rect.height)
22 del rect.size
23 print(rect.width,rect.height)
24 
25 class Retangle:
26     def __init__(self,width,height,length):
27         self.width = width
28         self.height = height
29         self.length = length
30     def getsize(self):
31         return self.width,self.height,self.length
32     def setsize(self,size):
33         self.width,self.height,self.length = size
34     def delsize(self):
35         self.width,self.height,self.length = 0,0,0
36     size = property(getsize,setsize,delsize,'描述事物的屬性')
37 rec = Retangle(88,56,72)
38 print(rec.size)
39 rec.size = 85,45,23
40 print(rec.size)
41 print(rec.width,rec.length,rec.height)
42 del rec.size
43 print(rec.size)
44 
45 
46 class User:
47     def __init__(self,first,last):
48         self.first = first
49         self.last = last
50     def getfullname(self):
51         return self.first + ',' + self.last
52     def setfullname(self,fullname):
53         name = fullname.split(',')
54         self.first = name[0]
55         self.last = name[1]
56     fullname = property(getfullname,setfullname)
57 u = User('','悟空')
58 print(u.fullname)
59 print(u.first,u.last)
60 u.fullname = 'smith,jackey'
61 print(u.fullname)
62 print(u.first,u.last)

 隱藏和封裝

 1 class User:
 2     def __hide(self):
 3         print("示范隱藏的方法")
 4     def getname(self):
 5         return self.__name
 6 
 7     def setname(self,name):
 8         if len(name) < 3 or len(name) > 8:
 9             raise ValueError("輸入長度在3-8之間的")
10         self.__name = name
11     name = property(getname,setname) #參數必須先
12 
13     def getage(self):
14         return self.__age #可以和property的屬性區分開來
15     def setage(self,age):
16         if age < 11 or age > 70:
17             raise ValueError("年齡在11到70歲之間")
18         self.__age = age
19     age = property(getage,setage)
20 
21 u = User()
22 u.name = "fuck"
23 print(u.name)
24 print(u.getname())
25 u.age = 56
26 print(u.getage())

set和get函數方法順序無關,但是property函數內參數,必須是先讀取后寫入的順序。

繼承

#繼承
class Fruit:
    def info(self,weight): #如果后面有參數,函數內就必須初始化,如果沒參數,后面初始化不同寫
        self.weight = weight
        print("this fruit weigh %s" % (self.weight))
class Food:
    def taste(self):
        print("不同食物,口味不同")
class Apple(Fruit,Food):
    pass
a = Apple()
a.info(25)
a.taste()

 

 1 #當父類方法名字重合,選擇第一個
 2 class Item:
 3     def info(self):
 4         print("這是一個商品")
 5 class Product:
 6     def info(self):
 7         print('這是一個工藝')
 8 
 9 class Mouse(Product,Item): #父類順序,如果有相同的方法,先調用一個參數
10     pass
11 
12 m = Mouse()
13 m.info()

父類方法重寫

 1 class Bird:
 2     def fly(self):
 3         print("我在天空里自由自在地飛翔")
 4 class Ostrich(Bird):
 5     #重寫Bird類的fly()方法
 6     def fly(self):
 7         print("我只能在地上奔跑")
 8 #創建Ostrich對象
 9 os = Ostrich()
10 #執行Ostrich對象的fly(),將輸出“我只能在地上奔跑”
11 os.fly()

 

 子類重寫父類方法,那如何調用父類被重寫的方法。

 1 class BaseClass:
 2     def foo(self):
 3         print("父類中定義的foo方法")
 4 class SubClass(BaseClass):
 5     def foo(self):
 6         print("子類中定義的foo方法")
 7     def bar(self):
 8         print("執行bar方法")
 9         self.foo()
10         #通過類名調用父類被重寫的方法
11         BaseClass.foo(self)
12 sc = SubClass()
13 sc.bar()

 使用super函數調用父類構造方法

 1 class  Employee:
 2     def __init__(self,salary):
 3         self.salary = salary
 4     def work(self):
 5         print("普通員工正在寫代碼,工資是:",self.salary)
 6 class Customer:
 7     def __init__(self,favorite,address):
 8         self.favorite = favorite
 9         self.address = address
10     def info(self):
11         print("我是一個顧客,我的愛好是:%s,地址是%s" % (self.favorite,self.address))
12         #manager 繼承了Employee,Customer
13 class Manager(Customer):
14     def __init__(self,favorite,address):
15         print("manager的構造方法")
16         #通過super函數調用父類的構造方法
17         super(Manager,self).__init__(favorite,address)
18 
19         
20 
21 m = Manager("IT","beijing")
22 
23 m.info()
24 
25 class Fooparent:
26     def __init__(self):
27         self.parents = 'I\'m the parent' # 5
28         print("Parent11") # 1
29     def bar(self,message):
30         print("%s from Parent"% message) # 3
31 class FooChild(Fooparent):
32     def __init__(self):
33         super(FooChild,self).__init__()
34         print("child22") # 2
35     def bar(self,message):
36         super(FooChild,self).bar(message)
37         print("Child bar function") #4
38         print(self.parents)
39 if __name__ == "__main__":
40     foochild = FooChild() #創建一個子類對象
41     foochild.bar("helloworld")
42 
43 #從上面可見先執行父類構造函數的打印函數,在執行子類打印函數,然后根據調用執行父類函數

 

python動態屬性與slots

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

def walk_func(self):
    print("%s慢慢走過每一片草地"% self.name)
d1 = Cat('Marry')
d2 = Cat('Kitty')
Cat.walk = walk_func
d1.walk()
d2.walk()

#__slot__限制動態添加的屬性和方法
class Dog:
    __slots__ = ('walk','age','name')
    def __init__(self,name):
        self.name = name
    def test():
        print("預先定義好的test方法")
d = Dog('Snoogy')
d.age = 5
print(d.age)
# d.weight = 24報錯
Dog.walk = walk_func
d.walk()
Dog.bar = lambda self:print("abc")
d.bar()

type函數定義類

def fn(self):
    print("fn函數")
#使用type函數定義Dog類
Dog = type('Dog',(object,),dict(walk = fn,age = 6))
#創建dog對象
d = Dog()
#分別查看d.dog的類型
print(type(d))
print(type(Dog))
print(type(Dog()))
print(type(d.walk))
d.walk()
print(d.age)
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM