python基础类型,Python基础-类
Python基礎-類
@(Python)[python, python基礎]
寫在前面
如非特別說明,下文均基于Python3
摘要
本文重點講述如何創建和使用Python類,綁定方法與非綁定方法的區別,以及Python的多態與簡單繼承。
1. 面向對象編程
1.1 對象和類
面向對象這種思想其實只是人類思維在程序設計領域的一種自然延伸。程序設計領域將現實世界中事物自然延伸為“對象”,事物擁有其屬性和作用,對象也一樣,擁有屬性以及方法;復雜的面向對象程序就是基于一個個基本的對象,相互交織,構造一個完整的對象生態。
現實世界中“類”這個概念其實并不顯著,但是也存在。類在面向對象中是對一個對象集合的抽象,抽象集合中對象共有的屬性,方法構成類。通過類可以構建具體的對象。
1.2 一切皆對象
Python哲學是
一切皆對象
在使用Python編程和學習時,需時刻秉持這一思想。
2. 定義Python類&對象
2.1 最簡單的python類
Python類定義的句法很簡單,關鍵字class后接合法類名即可;最簡單的Python類如下:
class Person:
pass
類對象
Python一切皆對象,類也不例外。Python解釋器在解釋完類定義這段代碼時,在當前作用域創建了一個類對象用來表示該類,并使用名字Person指向該類對象;
使用類對象,可以進行以下操作:
實例化
屬性引用
實例化&實例對象
實例化就是用類對象創建一個實例對象過程,實例化的結果是實例對象;語法如下:
p = Person()
以上語句在當前作用域創建Person類對象的實例對象,并使用名字p指向該對象。
屬性引用
引用對象的屬性非常簡單,使用obj.attr的方式就可以引用對象obj的屬性attr。
2.2 屬性綁定
只有對象綁定過屬性之后,才能引用該屬性。Python是動態語言,可以在可變對象創建之后為對象綁定屬性。
類對象的綁定屬性被稱為類變量;實例對象的綁定屬性被稱為實例變量。通常來說,實例變量是對于每個實例都獨有的數據,而類變量是該類所有實例共享的屬性和方法。
由于Python是動態語言,因此可以在對象創建之后修改對象的信息。
可以在Person類對象創建之后綁定屬性:
Person.school = 'Whu University'
也可以在Person實例對象創建之后綁定屬性:
p = Person()
p.name = 'Richard'
p.age = 20
def print_info(p):
print(p.name, p.age)
print_info(p)
更多關于類變量與實例變量的區別與聯系,參考 Python基礎-類變量和實例變量
3. 封裝
如果在對象創建之后再根據需要綁定屬性,并且在外部函數隨意訪問實例對象的屬性,那么類機制的優點就無法體現出來了。在實踐中,我們一般在定義類時就決定了類變量與實例變量。
3.1 隱藏細節
通常來說,對象的使用者只需要關心對象能“做什么”,而不需要也不應該關心“怎么做”,這就是封裝了。按照封裝的思想,一個Python類應該向外提供接口,并隱藏接口實現細節:
class Person:
school = 'Whu University' # 類屬性綁定
def __init__(self, name, age):
self.name = name # 實例屬性綁定
self.age = age # 實例屬性綁定
def print_info(self): # 暴露公共接口
print(self.name, self.age)
p = Person('Richard', 20)
p.print_info()
如上,將屬性的綁定放到類定義中,并向外提供了接口。然而不幸的是,在類外部還是可以引用到類實例的屬性。
3.2 綁定方法與非綁定方法
通過直接將print_info打印出來,可以直觀看到綁定方法與非綁定方法的區別:
print(Person.print_info)
print(p.print_info)
output:
>
可以看到類對象Person的函數print_info是個function;而實例對象p的方法print_info是個bound method綁定方法。
也可以查看它們的類型:
print(type(Person.print_info)) #
print(type(p.print_info)) #
一般來說,非綁定方法也叫函數,綁定方法簡稱方法。綁定方法的綁定,在于函數與特定的對象綁定在了一起。
引用非數據屬性的實例屬性時,會搜索它對應的類。如果名字是一個有效的函數對象,Python會將實例對象連同函數對象打包到一個抽象的對象中并且依據這個對象創建方法對象:這就是被調用的方法對象(綁定方法)。當使用參數列表調用方法對象時,會使用實例對象以及原有參數列表構建新的參數列表,并且使用新的參數列表調用函數對象。
那么,以綁定形式調用方法時,第一個參數不是顯式傳遞的,而是解釋器隱式傳遞的:
p.print_info()
同樣,也可以通過調用綁定方法的函數版本達到相同的目的:
Person.print_info(p) # 函數必須顯式傳遞參數
3.3 魔法方法__init__
方法__init__是一個充滿魔力的方法,一般以雙下劃線開頭并且結尾的方法對于Python都有特殊的意義,在滿足條件的時候被調用。
__init__就是一個構造方法,在類對象的實例化過程中被調用,即在p = Person()這條語句中,Python解釋器自動調用了__init__方法。
一般來說,使用__init__方法來初始化實例對象,即為實例對象綁定屬性。
3.4 綁定方法參數self
注意到類Person定義的兩個方法中都有一個占據第一個參數位置,名為self的參數。其實這是一個特殊參數:當調用綁定方法時,調用實例對象會被Python解釋器作為第一個參數來調用綁定方法。
因此,重要的是參數位置,在綁定方法中,第一個參數總是代表當前調用實例對象,與參數名字無關。但是,self的字面意思是自身的意思,這個名字能很好的體現第一個參數的實際意義。
在類中,類的局部作用域與函數的局部作用域是不能相互訪問的,詳見:Python進階 - 命名空間與作用域。因此,函數直接只能以對象引用的方式訪問其他屬性,這也是對外提供的接口都有self參數的原因。
class Person:
school = 'Whu University'
def __init__(self, name, age):
self.name = name
self.age = age
# print(school) 類屬性school是不能直接訪問的,需要用Person.school的形式
def print_info(self): # 如果沒有self,將訪問不到name和age
print(self.name, self.age)
3.5 私有化
Python并沒有語法支持屬性的私有化,但是Python可以使用“名稱變化術”改變私有屬性的名字:
class Person:
__school = 'Whu University'
def __init__(self, name, age):
self.__name = name
self.__age = age
def print_info(self):
print(self.__name, self.__age)
p = Person('Richard', 20)
p.print_info()
print(p.__dict__) # {'_Person__name': 'Richard', '_Person__age': 20}
通常來說,Python解釋器會將類定義中所有以__開頭的屬性變名,具體變名的規則由解釋器決定,目前的CPython解釋器是在前面加_類名的方式。
誠然,仍然可以使用變名后的屬性名,如此處的_Person__name, _Person__age來訪問實例對象的屬性,但是通常不建議這么做。因為變名的規則是解釋器決定的,如果變更解釋器,變名可能就不一樣了;再則,變名是類作者給類使用者的一個強力信號,不建議使用者直接訪問屬性。
一般地,還可以在屬性名前加前綴_,如_name, _age,來表達私有屬性。這種方式不會引起解釋器的變名,但是發出了不應該直接訪問這些屬性的信號。同時,這種規則的屬性不會被from module import *的方式導入。
4. 多態
多態意味著就算不知道變量所引用的對象的類型,還是能夠對它進行操作,而它會根據對象的實際類型的不同表現出不同的行為。
4.1 其他語言的多態
在一些高級語言中,為實現多態。首先會定義一個接口,然后實現若干繼承接口的的具體類,以頂層的接口來引用具體的接口實現,在運行時根據具體實現的不同表現出不同的行為:
interface Top {
void bar();
}
class A implements Top {
public void bar(){
// implementing of A
}
}
class B implements Top {
public void bar(){
// implementing of B
}
}
void foo(Top t) {
t.bar()
}
這里foo方法根據參數t實際類型的不同會表現出不同的行為。
4.2 Python的多態
Python的多態思想與上面是相同的,但是在實現上有所不同。Python中的多態是基于對象的行為,而不像其他語言是基于父類或者接口。Python雖然是強類型語言,但是Python的變量是沒有類型的,因此Python多態不需要一個頂層的接口。只需要實際的對象擁有需要的屬性即可:
class FooLike1:
def wow(self):
print('foo in FooLike1')
class FooLike2:
def wow(self):
print('foo in FooLike2')
def bar(foo):
foo.wow()
bar(FooLike1()) # foo in FooLike1
bar(FooLike2()) # foo in FooLike2
當然參與多態的對象有共同父類也是可以的,重點是都有參與多態需要的函數。Python中的多態要靈活得多;只要對象擁有指定方法,就可以參與多態,這種對象成為“like”對象,如和file對象擁有read方法的對象是file like對象,可以參與到關于read的多態中。
5. 繼承
繼承是一個懶惰的行為,子類可以不勞而獲從父類獲取必要信息。如現在有一個Person類,擁有名字和年齡屬性,現在要創建一個Student類,也有名字和年齡,并且增加和學號信息;通過繼承,可以從Person不勞而獲一些信息。
5.1 如何繼承
Python的繼承語法也很簡單,只需要在類名后跟括號,括號中寫入要繼承的類即可:
class DerivedClassName(BaseClassName):
.
.
.
當然,如果是多繼承,在繼承列表里添加即可:
class DerivedClassName(BaseClassName1, BaseClassName2, ..., BaseClassNamen):
.
.
.
5.1 繼承到了什么
那么,子類到底從父類繼承到了什么呢?一個直觀的例子可以看出:
class Person:
__school = 'Whu University'
def __init__(self, name, age):
self.__name = name
self.__age = age
def print_info(self):
print(self.__name, self.__age)
def test():
pass
class Student(Person):
def __init__(self):
pass
print(dir(Student))
s = Student()
print(dir(s))
output:
['_Person__school', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'print_info', 'test']
=======分割線=======
['_Person__school', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'print_info', 'test']
可以看出,子類類對象繼承到了父類類對象的所有東西,包括類屬性,函數。但是子類實例對象沒有繼承到父類實力對象的實例屬性,即 __name和__age沒有繼承到。
我們說過,屬性只有綁定之后才能引用,顯然,在子類中沒有調用父類的__init__方法,父類的實例對象的屬性自然沒有初始化;因此只要在子類中調用父類的構造方法,就可以繼承到實例屬性了:
class Student(Person):
def __init__(self, name, age, stu_id):
self.stu_id = stu_id
super().__init__(name, age)
子類如何初始化父類是一個大問題,單繼承比較簡單,涉及到多繼承的初始化就比較復雜了。
5.2 獲取繼承關系
Python有兩個可以判斷繼承關系的內建函數:
使用isinstance()檢查實例的類型:isinstance(obj, int),當且僅當obj.class是int或者派生與int的類時,返回True
使用issubclass()檢查類的繼承關系:issubclass(bool, int)返回True,因為bool是int的子類。然而issubclass(float, int)返回False,因為float不是int的子類。
總結
以上是生活随笔為你收集整理的python基础类型,Python基础-类的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: dedecms模版php,好织梦-专业d
- 下一篇: 从php传过来的是字符串吗,PHP 字符