#7 class,OOP, 類型判斷

python中類的聲明和js中基本相似,傳統(tǒng)OOP的3大特征:封裝,繼承,多態(tài),python也支持。

一.類的聲明

看python類聲明之前,先看看js中類的聲明(ES6特性,js中的類的實質(zhì)是函數(shù)),我們對比著看

# extends Object可以省略,則super()同時也省去
class Student extends Object {
  constructor(name, age) {
    super();
    this.name = name;
    this.age = age;
  }
  info() {
    console.log(`your name: ${this.name}, your age: ${this.age}`)
  }
}
# 使用
var james = new Student('James', 26)
james.info()
your name: James, your age: 26

在python中類的聲明,同樣使用關(guān)鍵詞 class:

class Student(object):
  def __init__(self, name, age):
    self.name = name
    self.age = age
  def info(self):
    print('your name: %s, your age: %s' % (self.name, self.age))
# 使用
james = Student('James', 26)
james.info()
your name: James, your age: 26    

類比起來看,可以看出兩者的語法基本類似,不同之處在于

  • python中的 __init__ 相當(dāng)于js中的構(gòu)造器函數(shù)constructor
  • python中類的后面括號直接添加父類: Student(object), 默認(rèn)父類是object,可以省略
  • python中的關(guān)鍵詞 self 相當(dāng)于 this, 表示context; 但是python中的self需要顯式的寫出來,并且后面的函數(shù)也需要寫出來,比如info(self)
  • python中實例化一個對象不需要使用 'new' 關(guān)鍵詞來調(diào)用構(gòu)造器

二.類屬性的聲明規(guī)范

python的命名形式一般有以下幾種方式:

  • __xxx: 表示私有的,不能直接訪問
  • __xxx__: 特殊變量,可以直接訪問,但是python很多內(nèi)部屬性以這種形式,命名的時候應(yīng)避免沖突
  • _xxx: 實例變量名,可以直接訪問,但是約定俗成的這是私有的,不要隨便訪問
  • xxx: 普通的命名,可以隨意訪問

__xxx

如果想要類的屬性不被訪問到,可以使用屬性前面添加 __ 的方式,這樣屬性就變?yōu)榱怂接袑傩?/p>

class Student(object):
  def __init__(self, name, age):
    self.__name = name
    self.__age = age
  def info(self):
    print('your name: %s, your age: %s' % (self.__name, self.__age))

>>> s = Student('James', 28)
>>> s.__name
# 報錯
AttributeError: 'Student' object has no attribute '__name'

這種方式看起來將屬性隱藏了,其實python內(nèi)部將屬性名變?yōu)榱?_className__propertyName 的形式,所以如果要獲取上面的 __name 屬性,可以這樣做:

>>> s._Student__name
James
>>> s._Student__age
28

如果想要修改這些私有的屬性,一般通過的是 set, get 方法(python需要自定義,所以名字可以隨意),在這2個方法中可以添加自己的一些邏輯:

class Student(object):
  ...
  def get_name(self):
    return self.__name
  def set_age(self, age):
    if self.__age < 0:
      print('age invalid')
      raise ValueError('age invalid')
    self.__age = age  

三.繼承和多態(tài)

上面的一些函數(shù)對數(shù)據(jù)的處理,實際上就是封裝的一種表現(xiàn)形式。下面來談?wù)劺^承和多態(tài)。

繼承

在最上面已經(jīng)談到了python繼承的基本形式,繼承是為了實現(xiàn)代碼的重用和抽象的提取,下面來看個具體的示例

# 省份
class Province(object):
  def __init__(self, proname):
    self.proname = proname

  def proInfo(self):
    print('我來自 %s' % self.proname)

# 城市
class City(Province):
  def __init__(self, proname, cityname):
    # 調(diào)用父類的構(gòu)造器
    # 等同于 self.proname = proname
    Province.__init__(self.proname) 
    self.cityname = cityname
  def cityInfo(self):
    print('我來自%s省 - %s市' % (self.proname, self.cityname))

>>> wuhan = City('湖北', '武漢')
# 調(diào)用父類的方法
>>> wuhan.proInfo()
我來自湖北

# 調(diào)用自身的方法
>>> wuhan.cityInfo()
我來自湖北省 - 武漢市

多態(tài)

多態(tài)的本質(zhì)就是當(dāng)一個函數(shù)能夠傳入父類對象,則也可以傳入其子類對象,實現(xiàn)方法的重用

比如在上面繼承的例子后面再定義一個函數(shù):

def f(x):
  f.proInfo()

# 父類Province中有proInfo方法,此處傳入一個Province的實例毫無問題
# 又因為City繼承自Province對象,所以傳入一個City的實例也是可以的
>>> p = Province('hubei')
>>> f(p)

>>> c = City('hubei', 'wuhan')
f(c)

四.靜態(tài)屬性

靜態(tài)屬性就是由類直接調(diào)用的屬性,因為python的動態(tài)特性,使用時需要注意一些問題,下面會講到

首先,聲明靜態(tài)屬性的方式,直接將屬性寫在類中,這一點和js一致:

class Student(object):
  ...

  name = 'james' # 靜態(tài)屬性
  def sayHi():   # 靜態(tài)方法,不使用self
    print('Hi')

# 使用
>>> Student.name
'james'  
>>> Student.sayHi()
'Hi'

但是對于實例來說,如果使用靜態(tài)屬性,實例會向上查找

>>> s = Student()
# 使用
>>> s.hello()
# 報錯

>>> s.name
'james'

# 但是 如果嘗試刪除該屬性會報錯
# 這是因為name屬性是屬于類本身的
>>> del s.name

# 再比如可以給實例s也添加一個name的屬性
>>> s.name = 'kobe'
>>> s.name
kobe
>>> Student.name
james

五.獲取對象信息

主要使用以下幾種方式來獲取一個對象的信息:

  • type(): 查看一個對象的類型
  • is | not: 判斷一個對象是不是什么
  • isinstance(obj, DataType): 判斷是不是某個對象的實例
  • types: 對象屬性(需要 import types)
  • hasattr() | getattr() | setattr(): 獲取對象的屬性
  • dir(DataType): 獲取該類型的所有方法,例如獲取所有字符串方法,dir('abc')

type()

直接返回是什么類型

>>> type('123') == str
True

>>> type(123)
<class 'int'>

>>> type(12) is int
True

isinstance

是不是某個類的實例

>>> isinstance('abc', str)
True

# 上面的Province 和 City
>>> isinstance(city, Province)
True
>>> isinstance(city, City)
True

types

一個對象具體是什么類型,可以使用 types 來判斷

import types # 需要引入該模塊

def f():
  pass

>>> type(f) == types.FunctionType
True

>>> type(lambda x: x) == types.LambdaType

>>> type(abs) == types.BuiltinFunctionType

>>> type((x for x in range(10))) == type.GeneratorType

這個 'types' 有很多中類型, types種類

另外判斷一個對象是不是函數(shù),還可以使用 callable 來判斷

def fn():
  pass

>>> callable(fn)  
True

# 以前版本可以使用 'hasattr' 判斷對象是否存在內(nèi)部屬性 '__call__'
>>> hasattr(fn, '__call__')
True

hasattr getattr setattr

分別相當(dāng)于js中的'hasOwnProperty()', 'getOwnPropertyDescriptor()', 'defineProperty()' (類似)

示例:

class MyObject(object):
  def __init__(self):
    self.x = 9
  def power(self):
    return self.x * self.x

>>> obj = MyObject()

>>> hasattr(obj, 'x')    
True

>>> hasattr(obj, 'y')
False

>>> setattr(obj, 'y', 18)
>>> getattr(obj, 'y')
18

如果使用getattr嘗試獲取一個不存在的屬性,會報錯, 可以傳入一個默認(rèn)的返回值

>>> getattr(obj, 'z')
AttributeError: 'MyObject' object has no attribute 'z'

# 如果不存在返回一個默認(rèn)值,避免報錯
>>> getattr(obj, 'z', -1)
-1

總結(jié)

本章主要談python面向?qū)ο蟮幕纠碚摚ǎ?/p>

  • 如何聲明一個類
  • OOP的3大特性:封裝,繼承,多態(tài)
  • python的命名規(guī)范,如何聲明一個私有變量, 靜態(tài)屬性等
  • 類型判斷比較全的總結(jié)
  • 使用 hasattr getattr setattr 來獲取一個對象的信息

2017年3月7日 15:12:52

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 要點: 函數(shù)式編程:注意不是“函數(shù)編程”,多了一個“式” 模塊:如何使用模塊 面向?qū)ο缶幊蹋好嫦驅(qū)ο蟮母拍睢傩浴?..
    victorsungo閱讀 1,697評論 0 6
  • 定義類并創(chuàng)建實例 在Python中,類通過 class 關(guān)鍵字定義。以 Person 為例,定義一個Person類...
    績重KF閱讀 4,110評論 0 13
  • # 第一優(yōu)先級規(guī)則聲明: # 除了夢境,每一個意識主進(jìn)程都必須與一個身體參與的機(jī)械進(jìn)程相匹配,否則結(jié)束意識主進(jìn)程。...
    李洞BarryLi閱讀 4,208評論 0 1
  • Python進(jìn)階框架 希望大家喜歡,點贊哦首先感謝廖雪峰老師對于該課程的講解 一、函數(shù)式編程 1.1 函數(shù)式編程簡...
    Gaolex閱讀 6,014評論 6 53
  • 常到南山走一走,一定活到九十九; 南山空氣真是好,山里人們也實在。
    木子春閱讀 203評論 0 1

友情鏈接更多精彩內(nèi)容