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