文章目錄
- 面向對象程式設計
-
- 類和執行個體
- 通路限制
- 繼承和多态
-
- 鴨子類型
- 擷取對象資訊
-
- 使用type()判斷
- 使用isinstance
- 使用dir()
- getattr()`、`setattr()`以及`hasattr()
- 執行個體屬性和類屬性
面向對象程式設計
類和執行個體
和Java一樣,把類作為程式設計的基本機關。
以Class為基本機關,裡面包括屬性和方法。
例如,學生類:
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
def print_score(self):
print('%s: %s' % (self.name, self.score))
括号中的(Object)指的是繼承下來的類。
bart = Student('Bart Simpson', 59)
lisa = Student('Lisa Simpson', 87)
bart.print_score()
lisa.print_score()
此處的bart是類的一個執行個體化,也就是通過類創造了對象。
通過定義一個特殊的
__init__
方法,在建立執行個體的時候,就把
name
,
score
等屬性綁上去:
注:__init__
方法的第一個參數永遠是
self
,表示建立的執行個體本身,是以,在
__init__
方法内部,就可以把各種屬性綁定到
self
,因為
self
就指向建立的執行個體本身。(類似于java的this)
無論是什麼方法,第一個參數都得是self
通路限制
如果要讓内部屬性不被外部通路,可以把屬性的名稱前加上兩個下劃線
__
,在Python中,執行個體的變量名如果以
__
開頭,就變成了一個私有變量(private),隻有内部可以通路,外部不能通路
注:在Python中,變量名類似
__xxx__
的,也就是以雙下劃線開頭,并且以雙下劃線結尾的,是特殊變量,特殊變量是可以直接通路的,不是private變量,是以,不能用
__name__
、
__score__
這樣的變量名。
繼承和多态
繼承:直接擷取父類的屬性和變量
多态:子類方法可以覆寫父類同名方法,同時,父類作為參數的函數或者方法都可以不加修改地正常運作,原因就在于多态。
動态語言的鴨子類型特點決定了繼承不像靜态語言那樣是必須的。
鴨子類型
調用對象不看類型,隻看方法。
比如:
function calculate(a, b, c) => return (a+b)*c
example1 = calculate (1, 2, 3)
example2 = calculate ([1, 2, 3], [4, 5, 6], 2)
example3 = calculate ('apples ', 'and oranges, ', 3)
print to_string example1
print to_string example2
print to_string example3
其中的example3其實是不合理的,因為對于calculate函數所針對的對象應當是一個數。但是,因為str類型支援‘+’,是以這個函數可以執行。
運作結果:
9
[1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6]
apples and oranges, apples and oranges, apples and oranges,
那麼,不要求str繼承int或者float類型,隻要支援*和+兩個方法就可以了。
再例如:
class Duck:
def quack(self):
print("這鴨子在呱呱叫")
def feathers(self):
print("這鴨子擁有白色與灰色羽毛")
class Person:
def quack(self):
print("這人正在模仿鴨子")
def feathers(self):
print("這人在地上拿起1根羽毛然後給其他人看")
def in_the_forest(duck):
duck.quack()
duck.feathers()
def game():
donald = Duck()
john = Person()
in_the_forest(donald)
in_the_forest(john) # 此處的代碼在java中無法運作,in_the_forset的參數是duck,但是john是個人。但是在python中這句代碼可以運作。python隻通路需要的方法,是以隻要函數中調用的方法在參數中存在就可以執行
game()
鴨子類型,讓python在不繼承的情況下,也可以使用多态的特性。
擷取對象資訊
使用type()判斷
用法:
>>> type(123)
<class 'int'>
>>> type(str)
<class 'type'>
>>> type('str')
<class 'str'>
要判斷一個對象是否是函數怎麼辦?可以使用
types
子產品中定義的常量:
>>> import types
>>> def fn():
... pass
...
>>> type(fn)==types.FunctionType # 函數類型
True
>>> type(abs)==types.BuiltinFunctionType
True
>>> type(lambda x: x)==types.LambdaType # lambdaType類型
True
>>> type((x for x in range(10)))==types.GeneratorType # 生成器類型
True
使用isinstance
insinstance()可以用來判斷執行個體和類之間的關系。
用法:isinstance(對象,類),如果對象屬于該類就是True,不屬于就是False
如果:
object -> Animal -> Dog -> Husky
則
>>> class Animal(object):
... pass
...
>>> class Dog(Animal):
... pass
...
>>> class Baby(Dog):
... pass
...
>>> MT = Baby()
>>> isinstance(MT, Dog)
True
使用dir()
使用dir()可以擷取一個對象所有的屬性和方法
>>> dir('abc')
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__ge
tnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__'
, '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold
', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigi
t', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition'
, 'removeprefix', 'removesuffix', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip',
'swapcase', 'title', 'translate', 'upper', 'zfill']
我們自己寫的類,也可以加一些常用方法,比如
__len__
方法傳回長度。在Python中,如果你調用
len()
函數試圖擷取一個對象的長度,實際上,在
len()
函數内部,它自動去調用該對象的
__len__()
方法,是以,下面的代碼是等價的。我們自己寫的類,如果也想用
len(myObj)
的話,就自己寫一個
__len__()
方法:
>>> class MyDog(object):
... def __len__(self):
... return 100
...
>>> dog = MyDog()
>>> len(dog)
100
getattr() 、
setattr() 以及
hasattr()
、
以及
用法:getattr(類對象,屬性)
getattr()傳回某個屬性的值,如果試圖擷取不存在的屬性,會抛出AttributeError的錯誤。
如果不想抛出錯誤,可以傳入一個default參數,如果屬性不存在,就傳回預設值。
setattr(對象,屬性,值)
添加一個屬性并設定他的值
hasattr()
判斷某對象是否存在一個屬性
例如,對于類Myobject:
>>> class MyObject(object):
... def __init__(self):
... self.x = 9
... def power(self):
... return self.x * self.x
...
>>> obj = MyObject()
>>> hasattr(obj, 'x') # 有屬性'x'嗎?
True
>>> obj.x
9
>>> hasattr(obj, 'y') # 有屬性'y'嗎?
False
>>> setattr(obj, 'y', 19) # 設定一個屬性'y'
>>> hasattr(obj, 'y') # 有屬性'y'嗎?
True
>>> getattr(obj, 'y') # 擷取屬性'y'
19
>>> obj.y # 擷取屬性'y'
19
>>> getattr(obj, 'z', 404) # 擷取屬性'z',如果不存在,傳回預設值404
404
執行個體屬性和類屬性
就類似Iava的靜态屬性。屬于類的屬性。
給執行個體綁定屬性的方法是通過執行個體變量,或者通過
self
變量:
class Student(object):
def __init__(self, name):# 通過self變量
self.name = name
s = Student('Bob')
s.score = 90 # 通過執行個體變量
同時,也可以給執行個體綁定一個方法。
>>> def set_age(self, age): # 定義一個函數作為執行個體方法
... self.age = age
...
>>> from types import MethodType # 這個類型是Python自帶的方法
>>> s.set_age = MethodType(set_age, s) # 給執行個體綁定一個方法
>>> s.set_age(25) # 調用執行個體方法
>>> s.age # 測試結果
25
通過Python自帶的方法進行綁定。
如果
Student
類本身需要綁定一個屬性呢?可以直接在class中定義屬性,這種屬性是類屬性,歸
Student
類所有:
class Student(object):
name = 'Student'
這個屬性,所有的對象都可以通路。