1. 程式人生 > >從java面向物件去分析python面向物件

從java面向物件去分析python面向物件

本文的前提是對java的面向物件語法和概念有一個基本的瞭解,試圖從這裡去學習理解python的面向物件。


一,用python去實現類

class Animal(object):
    
    kind = 2000
     
    def __init__(self, name):
        self.name = name
        
    def run(self):
        print('Animal is running...')

上面的語法表示:

  1. 建立Animal類 繼承object類
  2. __init__是初始化的方法,相當於java中的構造方法
  3. 新增一個run方法

兩點需要注意的地方:

  1. java中的this改用self代替,並且每個方法都要用self做為第一個引數,但是呼叫方法的時候可以省略self引數
  2. name是例項屬性,kind是類屬性

二,python可以動態的繫結屬性和方法

animal = Animal('佩奇')
print(animal.name)

animal.age = 7
print(animal.age)

Output:
佩奇
7

雖然上面的Animal類的定義中,只指出了一個例項屬性name,但是可以在建立物件對,直接新增一個age屬性進去。這一點對於java這種靜態語言是不可能的,如果學過js或者matlab估計會很容易理解吧。

甚至可以新增一個方法

def pao(self):
    print('Animal is pao...')
animal.pao = pao     #Animal.pao = pao

animal.pao(animal)    #animal.pao()

Output:
Animal is pao...

因為python的方法名可以理解成一個指向記憶體中方法的一個變數,所以原理上新增方法和新增屬性是一樣的。

如果這裡呼叫的時候不想傳參,需要把方法繫結到Animal類上 而不是物件上,如果就像繫結到物件上, 還不想傳參,那麼可以這樣

from types import MethodType

def pao(self):
    print('Animal is pao...')
animal.pao = MethodType( pao, animal)

animal.pao()

Output:
Animal is pao...

三,訪問許可權控制

java中有訪問修飾符,private,default,protected,public,每一個修飾符都有明確的可訪問範圍。

那麼python如何進行這種控制?在屬性名稱前加上__即可,程式碼如下

class Animal(object):

    kind = 2000

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

    def run(self):
        print('Animal is running...')


animal = Animal('佩奇')
print(animal.__name)

Output:
AttributeError: 'Animal' object has no attribute '__name'

可以看到,再直接訪問animal.name就會報錯,說Animal類中沒有name這個屬性

原理非常簡單,只是python直譯器把__name變成了_Animal__name(不同的編譯器會變成不同的名字),所以如果你一定要訪問的話,沒人攔得住你。

四,python多型和'鴨子型別'

多型的概念就不闡述了,先來看看程式碼

class Animal(object):
    def run(self):
        print('Animal is running...')

class Dog(Animal):
    def run(self):
        print('Dog is running')

def AnimalRun(animal):
    animal.run()

dog = Dog()

AnimalRun(dog)

Output:
Dog is running

乍一看,這段程式碼很正常,AnimalRun方法接收一個Animal類的例項,然後就可以呼叫這個例項的run方法了,也就是和java的多型一樣,針對介面或者父類程式設計即可滿足開閉原則。

但是,仔細看就會發現問題,AnimalRun方法的引數你是沒辦法在函式定義中限制類型的,因為python是動態語言,你傳什麼引數,它就是什麼型別,那麼只要這個型別有run方法,程式碼可以執行。也就是動態語言所謂的'鴨子型別':一個事物,看起來像鴨子,走起來像鴨子,那它就是鴨子。

從這一點去理解動態語言和靜態語言在多型上的差異比較好,那麼如果想要限制animal只能是Animal類的例項呢? 在程式碼中做限制即可

def AnimalRun(animal):
    if isinstance(animal, Animal):
        animal.run()

五,python獲取物件的資訊

這個事兒如果放在java裡,就有一個比較高大上的名字,叫'反射',當然反射還有其他的作用,比如動態建立物件。

python中用 hasattr(), getattr(), setattr() 三個來查詢,獲取,新增設定屬性方法。

animal = Animal('佩奇')

print(hasattr(animal, 'name'))
print(hasattr(animal, 'run'))
print(getattr(animal, 'age', '沒有age')) #沒有age屬性就返回 沒有
setattr(animal, 'age', 7)
print(animal.age)


Output:
True
True
沒有age
7

還可以用dir()來查詢所有的屬性方法

print(dir(animal))

Output:
['__class__', '__delattr__', '__dict__', '__dir__', ... , 'age', 'name', 'run']

經過這番對比分析,相信對python面向物件的瞭解,甚至對動態語言和靜態語言面向物件的基本區別應該比較清楚了