py15 面向對象編程

新式類與經典類的區別

python中類的發展java

➤截止到python2.1,只存在舊式類。舊式類中,類名和type是無關的:若是x是一箇舊式類,那麼x.__class__定義了x的類名,可是type(x)老是返回<type 'instance'>。這反映了全部的舊式類的實例是經過一個單一的叫作instance的內建類型來實現的,這是它和類不一樣的地方。python

➤新式類是在python2.2爲了統一類和實例引入的。一個新式類只能由用戶自定義。若是x是一個新式類的實例,那麼type(x)和x.__class__是同樣的結果(儘管這不能獲得保證,由於新式類的實例的__class__方法是容許被用戶覆蓋的)。程序員

➤Python 2.x中默認都是經典類,只有顯式繼承了object纔是新式類算法

➤Python 3.x中默認都是新式類,經典類被移除,沒必要顯式的繼承object數組

新式類與經典類的區別數據結構

➤新式類都從object繼承,經典類不須要。app

➤新式類相同父類只執行一次構造函數,經典類重複執行屢次。函數

➤區分方式:type(classname)工具

-新式類:type優化

-經典類: classobj

➤新式類的MRO(method resolution order 基類搜索順序)算法採用C3算法廣度優先搜索,而舊式類的MRO算法是採用深度優先搜索

#經典類
class A:
    def __init__(self):
        print 'this is A'

    def save(self):
        print 'come from A'

class B(A):
    def __init__(self):
        print 'this is B'

class C(A):
    def __init__(self):
        print 'this is C'
    def save(self):
        print 'come from C'

class D(B,C):
    def __init__(self):
        print 'this is D'

d1=D()
d1.save()  #結果爲'come from A

#新式類
class A(object): def __init__(self): print 'this is A'

    def save(self): print 'come from A'

class B(A): def __init__(self): print 'this is B'

class C(A): def __init__(self): print 'this is C'
    def save(self): print 'come from C'

class D(B,C): def __init__(self): print 'this is D' d1=D() d1.save() #結果爲'come from C'

類會產生新的名稱空間,用來存放類的變量名與函數名,能夠經過類名.__dict__查看

對於經典類來講咱們能夠經過該字典操做類名稱空間的名字(新式類有限制),但python爲咱們提供專門的.語法,點是訪問屬性的語法,類中定義的名字,都是類的屬性

# .專門用來訪問屬性,本質操做的就是__dict__
print(type(OldClass), OldClass.__dict__['school']) # 等於經典類的操做OldboyStudent.__dict__['school'] 
print(type(NewClass), NewClass.__dict__['school'])
OldClass.school='Oldboy123'  # 等於經典類的操做OldboyStudent.__dict__['school']='Oldboy'
print(OldClass.school)
# OldClass.x=1 #等於經典類的操做OldboyStudent.__dict__['x']=1
# del OldboyStudent.x #等於經典類的操做OldboyStudent.__dict__.pop('x')

(<type 'classobj'>, 'oldboy')
(<type 'type'>, 'oldboy')
Oldboy123

python爲類內置的特殊屬性

  • 類名.__name__# 類的名字(字符串)
  • 類名.__doc__# 類的文檔字符串
  • 類名.__base__# 類的第一個父類(在講繼承時會講)
  • 類名.__bases__# 類全部父類構成的元組(在講繼承時會講)
  • 類名.__dict__# 類的字典屬性(可以查看類或對象中的全部成員)
  • 類名.__module__#類定義所在的模塊
  • 類名.__class__# 實例對應的類(僅新式類中)
  • __slots__,內存優化,不建議使用
    '''
    1.__slots__是什麼:是一個類變量,變量值能夠是列表,元祖,或者可迭代對象,也能夠是一個字符串(意味着全部實例只有一個數據屬性)
    2.引子:使用點來訪問屬性本質就是在訪問類或者對象的__dict__屬性字典(類的字典是共享的,而每一個實例的是獨立的)
    3.爲什麼使用__slots__:字典會佔用大量內存,若是你有一個屬性不多的類,可是有不少實例,爲了節省內存可使用__slots__取代實例的__dict__
    當你定義__slots__後,__slots__就會爲實例使用一種更加緊湊的內部表示。實例經過一個很小的固定大小的數組來構建,而不是爲每一個實例定義一個
    字典,這跟元組或列表很相似。在__slots__中列出的屬性名在內部被映射到這個數組的指定小標上。使用__slots__一個很差的地方就是咱們不能再給
    實例添加新的屬性了,只能使用在__slots__中定義的那些屬性名。
    4.注意事項:__slots__的不少特性都依賴於普通的基於字典的實現。另外,定義了__slots__後的類再也不 支持一些普通類特性了,好比多繼承。大多數狀況下,你應該
    只在那些常常被使用到 的用做數據結構的類上定義__slots__好比在程序中須要建立某個類的幾百萬個實例對象 。
    關於__slots__的一個常見誤區是它能夠做爲一個封裝工具來防止用戶給實例增長新的屬性。儘管使用__slots__能夠達到這樣的目的,可是這個並非它的初衷。           更多的是用來做爲一個內存優化工具。
    
    '''
    class Foo:
        __slots__='x'
    
    
    f1=Foo()
    f1.x=1
    f1.y=2#報錯
    print(f1.__slots__) #f1再也不有__dict__
    
    class Bar:
        __slots__=['x','y']
        
    n=Bar()
    n.x,n.y=1,2
    n.z=3#報錯
    
    __slots__使用
  • 類名.__str__,類名.__repr,類名.__format__
  • #若是一個類中定義了__str__方法,那麼在打印 對象 時,默認輸出該方法的返回值,案例以下
    # Tools:Pycharm 2017.3.2
    # author ="wlx"
    __date__ = '2018/8/14 17:37'
    #_*_coding:utf-8_*_
    __author__ = 'Linhaifeng'
    format_dict={
        'nat':'{obj.name}-{obj.addr}-{obj.type}',#學校名-學校地址-學校類型
        'tna':'{obj.type}:{obj.name}:{obj.addr}',#學校類型:學校名:學校地址
        'tan':'{obj.type}/{obj.addr}/{obj.name}',#學校類型/學校地址/學校名
    }
    class School:
        def __init__(self,name,addr,type):
            self.name=name
            self.addr=addr
            self.type=type
    
        def __repr__(self):
            return 'School(%s,%s)' %(self.name,self.addr)
        def __str__(self):
            return '(%s,%s)' %(self.name,self.addr)
    
        def __format__(self, format_spec):
            # if format_spec
            if not format_spec or format_spec not in format_dict:
                format_spec='nat'
            fmt=format_dict[format_spec]
            return fmt.format(obj=self)
    
    s1=School('oldboy1','北京','私立')
    print('from repr: ',repr(s1))
    print('from str: ',str(s1))
    print(s1)
    
    '''
    str函數或者print函數--->obj.__str__()
    repr或者交互式解釋器--->obj.__repr__()
    若是__str__沒有被定義,那麼就會使用__repr__來代替輸出
    注意:這倆方法的返回值必須是字符串,不然拋出異常
    '''
    print(format(s1,'nat'))
    print(format(s1,'tna'))
    print(format(s1,'tan'))
    print(format(s1,'asfdasdffd'))
  • 類名.__init__ # 構造方法,經過類建立對象時,自動觸發執行。
  • 類名.__del__ # 析構方法,當對象在內存中被釋放時,自動觸發執行。注:此方法通常無須定義,由於Python是一門高級語言,程序員在使用時無需關心內存的分配和釋放,由於此工做都是交給Python解釋器來執行,因此,析構函數的調用是由解釋器在進行垃圾回收時自動觸發執行的
  • 類名. __call__  # 對象後面加括號或者 類()(),觸發執行,對象執行生成對象類的call,類()()執行父類的call
    注:構造方法的執行是由建立對象觸發的,即:對象 = 類名() ;而對於 __call__ 方法的執行是由對象後加括號觸發的,即:對象() 或者 類()()
    class Foo:
     
        def __init__(self):
            pass
         
        def __call__(self, *args, **kwargs):
     
            print '__call__'
     
     
    obj = Foo() # 執行 __init__
    obj()       # 執行 __call__
  • .__getitem__、__setitem__、__delitem__ # 用於索引操做,如字典。以上分別表示獲取、設置、刪除數據
    class Foo:
        def __init__(self,name):
            self.name=name
    
        def __getitem__(self, item):
            print(self.__dict__[item])
    
        def __setitem__(self, key, value):
            self.__dict__[key]=value
        def __delitem__(self, key):
            print('del obj[key]時,我執行')
            self.__dict__.pop(key)
        def __delattr__(self, item):
            print('del obj.key時,我執行')
            self.__dict__.pop(item)
    
    f1=Foo('sb')
    print(f1.__dict__)
    f1['age']=18    # 自動調用setitem
    f1['age1']=19
    print(f1.__dict__)
    res = f1['age']  # 自動調用getitem
    del f1.age1  # 自動調用delattr
    print(f1.__dict__)
    del f1['age']  # 自動調用delitem
    f1['name']='alex'
    print(f1.__dict__)
     ##############
    {'name': 'sb'}
    {'name': 'sb', 'age': 18, 'age1': 19}
    18
    del obj.key時,我執行
    {'name': 'sb', 'age': 18}
    del obj[key]時,我執行
    {'name': 'alex'}
    __setattr__,__delattr__,__getattr__
  • class Foo:
        x=1
        def __init__(self,y):
            self.y=y
    
        def __getattr__(self, item):
            print('----> from getattr:你找的屬性不存在')
    
    
        def __setattr__(self, key, value):
            print('----> from setattr')
            # self.key=value #這就無限遞歸了,你好好想一想
            # self.__dict__[key]=value #應該使用它
    
        def __delattr__(self, item):
            print('----> from delattr')
            # del self.item #無限遞歸了
            self.__dict__.pop(item)
    
    #__setattr__添加/修改屬性會觸發它的執行
    f1=Foo(10)
    print(f1.__dict__) # 由於你重寫了__setattr__,凡是賦值操做都會觸發它的運行,你啥都沒寫,就是根本沒賦值,除非你直接操做屬性字典,不然永遠沒法賦值
    f1.z=3
    print(f1.__dict__)
    
    #__delattr__刪除屬性的時候會觸發
    f1.__dict__['a']=3#咱們能夠直接修改屬性字典,來完成添加/修改屬性的操做
    del f1.a
    print(f1.__dict__)
    
    #__getattr__只有在使用點調用屬性且屬性不存在的時候纔會觸發
    f1.xxxxxx
    
    三者的用法演示

反射

hasattr,getattr,setattr,delattr

class BlackMedium:
    feature = 'Ugly'

    def __init__(self, name, addr):
        self.name = name
        self.addr = addr

    def sell_house(self):
        print('%s 黑中介賣房子啦,傻逼纔買呢,可是誰能證實本身不傻逼' % self.name)

    def rent_house(self):
        print('%s 黑中介租房子啦,傻逼才租呢' % self.name)


b1 = BlackMedium('萬成置地', '回龍觀天露園')

# 檢測是否含有某屬性
print(hasattr(b1, 'name'))
print(hasattr(b1, 'sell_house'))
print(hasattr(b1, 'age'))

# 獲取屬性
n = getattr(b1, 'name')
print(n)
func = getattr(b1, 'rent_house')
func()

# getattr(b1,'aaaaaaaa') #報錯
print(getattr(b1, 'aaaaaaaa', '不存在啊'))

# 設置屬性
setattr(b1, 'sb', True)
setattr(b1, 'show_name', lambda self: self.name + 'sb')
print(b1.__dict__)
print(b1.show_name(b1))

# 刪除屬性
delattr(b1, 'addr')
delattr(b1, 'show_name')
delattr(b1, 'show_name111')  # 不存在,則報錯

print(b1.__dict__)

模塊中使用反射 

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import sys


def s1():
    print 's1'


def s2():
    print 's2'


this_module = sys.modules[__name__]

hasattr(this_module, 's1')
getattr(this_module, 's2')

反射當前模塊成員

類的構造函數與實例化過程詳解

構造函數的做用爲:初始化類內的各類屬性

構造函數示例:

class Role(object): #定義一個類, class是定義類的語法,Role是類名
    def __init__(self,name,role,weapon,life_value=100,money=15000): #初始化函數,在生成一個角色時要初始化的一些屬性就填寫在這裏
        self.name = name #__init__中的第一個參數self,和這裏的self都 是什麼意思? 看下面解釋
        self.role = role
        self.weapon = weapon
        self.life_value = life_value
        self.money = money


    def sayhi(self):   # 爲何類內定義的函數第一個參數都是self?有什麼做用?
        print("hello,I am a dog, my name is ",self.name)

 如何理解self和實例化過程?

 理解self和實例化的過程,要先了解實例化的幾種方法。例如java中,java的實例化以下圖

 

main方法結束以後p就沒有了,指向堆內存的地址就斷開了,堆內存就釋放了

在java的實例化過程當中,建立對象語句先進對內存,成員變量置空,而後調用構造函數,初始化成員變量,而後把內存給棧內的對象p

python中的實例化

相比,在python中,實例化對象並無採用java式的實例化方式,下面講解python實例化方式和self的做用,有下面兩個實例化語句:

實例化時至關於給__init__()函數傳參,self自動傳,不須要手動寫。

r1 = Role('Alex','police','AK47’) #此時self 至關於 r1 ,  Role(r1,'Alex','police','AK47’)
r2 = Role('Jack','terrorist','B22’)#此時self 至關於 r2, Role(r2,'Jack','terrorist','B22’)

這裏你必定會問爲何會自動傳一個self參數呢,不是像java那樣吧實例化後的地址給r1麼?

其實你執行r1 = Role('Alex','police','AK47 )時,至關於把r1(也就是self)和各類參數一塊兒傳給了構造函數,而後python的解釋器幹了兩件事:
  1. 在內存中開闢一塊空間指向r1這個變量名(並非開闢空間存各類初始化信息而後傳地址)
  2. 調用Role這個類並執行其中的__init__(…)方法,至關於Role.__init__(r1,'Alex','police',’AK47’), 至關於把'Alex','police',’AK47’各成員變量都放到r1(self)的地址下,如此實現了成員變量和對象之間的關聯,關聯起來後,就能夠直接r1.name, r1.weapon 這樣來調用啦。因此,爲實現這種關聯,在調用__init__方法時,就必須把r1這個變量也傳進去,不然__init__不知道要把那3個參數跟誰關聯呀。
  3. 此時self.name = name , self.role = role 等等的意思就是要把這幾個值 存到r1的內存空間裏。
  4. 還沒完,這裏只是說構造函數中的self做用,還沒解釋爲何各個方法裏也全都有self

如今咱們知道了,實例化時經過構造函數,已經完成了成員變量和對象之間的關聯,可是,對象裏面尚未關聯函數(方法)啊?

其實這裏並無給對象r1關聯方法,而是用到類的方法時把r1(self)傳給該函數,而後經過函數裏的self.xxx進行使用,以下:

def buy_gun(self,gun_name):
    print(「%s has just bought %s」 %(self.name,gun_name) )

r1 = Role('Alex','police','AK47')
r1.buy_gun("B21」) #python 會自動幫你轉成 Role.buy_gun(r1,」B21")

執行結果

#Alex has just bought B21 
Python會自動的幫你把r1 賦值給self這個參數, 爲何呢? 由於,你在buy_gun(..)方法中可能要訪問r1的一些其它屬性, 好比這裏就訪問 了r1的名字,怎麼訪問呢?你得告訴這個方法呀,因而就把r1傳給了這個self參數,而後在buy_gun裏調用 self.name 就至關於調用r1.name 啦,若是還想知道r1的生命值 有多少,直接寫成self.life_value就能夠了。 說白了就是在調用類中的一個方法時,你得告訴人家你是誰。

類變量和實例變量

類變量:類變量直接定義在類中且在函數體以外,類變量在整個實例化的對象中是公用的。

實例變量:定義在方法中的變量,只做用於當前實例。(對象調用時,實例變量和類變量重名以實例變量爲先)

obj.name會先從obj本身的名稱空間裏找name,找不到則去類中找,類也找不到就找父類...最後都找不到就拋出異常 

class Role:
    n = 123  # 類變量
    n_list = []
    name = "我是類name"

    def __init__(self, name, role, weapon, life_value=100, money=15000):
        # 構造函數
        # 在實例化時作一些類的初始化的工做
        self.name = name  # r1.name=name實例變量(靜態屬性),做用域就是實例自己
        self.role = role
        self.weapon = weapon
        self.__life_value = life_value
        self.money = money

    def __del__(self):
        pass  # print("%s 完全死了。。。。" %self.name)

r1 = Role('Chenronghua', 'police',  'AK47')
r2 = Role('jack', 'terrorist', 'B22')

看到這裏時,你應該明白了python實例化時作了什麼,類變量和實例變量的區別,那麼問個問題來檢驗一下,在實例化以後,對對象r1賦一個新實例變量,是否符合語法?

答案是能夠,由於你已經實例化了,那麼實例變量已經存在了r1(self)下,使用r1.newattr = ‘123’至關於再給r1一個實例變量而已,因此能夠。
 
等等,還有一個問題,你已經知道實例化以後,對象能夠建立新的實例變量,那麼 對象能夠對類變量進行修改麼?相似r1.n = 123?
答案是不行, 由於r1.n = 123至關於又對r1建立了一個實例變量和類變量沒有關係啊!!
 
可是要注意對類變量列表形式的操做:
r1.n_list.append('123')
r2.n_list.append('456')
print(r2.n_list)

# ['123', '456']

注意這種.append()形式並無對r1建立新的實例變量,而是直接對類變量n_list進行操做

可是下面這種則是建立了r1的新實例變量

r1.n_list = ['879']
r2.n_list.append('456')
print(r2.n_list)

# ['456']

這種形式,至關於建立了r1的新實例變量,對類變量n_list沒有影響

你覺得你理解了麼,試試下面的代碼,看看輸出和你預料的是否是同樣的:

class D:
    a = 10
    def __init__(self):
        self.b = 10

d1 = D()
d2 = D()
d1.a += 1
print(d1.a, d2.a)
D.a = 100
print(d1.a, d2.a)

  這兩個print的輸出都是什麼?結果以下:

11 10
11 100

  若是你的預料的不對,去上面幾行找答案吧

如何給對象添加類中沒有的函數

不推薦使用,知道便可

class A:
    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

def speak(self):  # 綁定和參數有關的函數
    print('%s is roaring' %self.name)

def call():  # 綁定和參數無關的函數
    print('someone is roaring')
a = A('wei', 18, 'male')
a.speak = speak
a.speak(a)  # 參數有關,要傳self(對象自身)
a.call = call
a.call()
print(a.__dict__)

################
wei is roaring
someone is roaring
{'name': 'wei', 'age': 18, 'sex': 'male', 'speak': <function speak at 0x000002689C702E18>, 'call': <function call at 0x00000268ABDBE268>}
相關文章
相關標籤/搜索