面向物件之封裝
一 引子
從封裝本身的意思去理解,封裝就好像是拿來一個麻袋,把小貓,小狗,小王八,還有alex一起裝進麻袋,然後把麻袋封上口子。照這種邏輯看,封裝=‘隱藏’,這種理解是相當片面的
二 先看如何隱藏
在python中用雙下劃線開頭的方式將屬性隱藏起來(設定成私有的)
#其實這僅僅這是一種變形操作且僅僅只在類定義階段發生變形 #類中所有雙下劃線開頭的名稱如__x都會在類定義時自動變形成:_類名__x的形式: class A: __N=0 #類的資料屬性就應該是共享的,但是語法上是可以把類的資料屬性設定成私有的如__N,會變形為_A__N def __init__(self): self.__X=10 #變形為self._A__X def __foo(self): #變形為_A__foo print('from A') def bar(self): self.__foo() #只有在類內部才可以通過__foo的形式訪問到. #A._A__N是可以訪問到的,
#這種,在外部是無法通過__x這個名字訪問到。
這種變形需要注意的問題是:
1.這種機制也並沒有真正意義上限制我們從外部直接訪問屬性,知道了類名和屬性名就可以拼出名字:_類名__屬性,然後就可以訪問了,如a._A__N,
2.變形的過程只在類的定義時發生一次,在定義後的賦值操作,不會變形
3.在繼承中,父類如果不想讓子類覆蓋自己的方法,可以將方法定義為私有的
#正常情況 >>> class A: ... def fa(self): ... print('from A') ... def test(self): ... self.fa() ... >>> class B(A): ...View Codedef fa(self): ... print('from B') ... >>> b=B() >>> b.test() from B #把fa定義成私有的,即__fa >>> class A: ... def __fa(self): #在定義時就變形為_A__fa ... print('from A') ... def test(self): ... self.__fa() #只會與自己所在的類為準,即呼叫_A__fa ... >>> class B(A): ... def __fa(self): ... print('from B') ... >>> b=B() >>> b.test() from A
三 封裝不是單純意義的隱藏
封裝的真諦在於明確地區分內外,封裝的屬性可以直接在內部使用,而不能被外部直接使用,然而定義屬性的目的終歸是要用,外部要想用類隱藏的屬性,需要我們為其開闢介面,讓外部能夠間接地用到我們隱藏起來的屬性,那這麼做的意義何在???
1:封裝資料:將資料隱藏起來這不是目的。隱藏起來然後對外提供操作該資料的介面,然後我們可以在介面附加上對該資料操作的限制,以此完成對資料屬性操作的嚴格控制。
class Teacher: def __init__(self,name,age): # self.__name=name # self.__age=age self.set_info(name,age) def tell_info(self): print('姓名:%s,年齡:%s' %(self.__name,self.__age)) def set_info(self,name,age): if not isinstance(name,str): raise TypeError('姓名必須是字串型別') if not isinstance(age,int): raise TypeError('年齡必須是整型') self.__name=name self.__age=age t=Teacher('egon',18) t.tell_info() t.set_info('egon',19) t.tell_info()View Code
2:封裝方法:目的是隔離複雜度
封裝方法舉例:
1. 你的身體沒有一處不體現著封裝的概念:你的身體把膀胱尿道等等這些尿的功能隱藏了起來,然後為你提供一個尿的介面就可以了(介面就是你的。。。,),你總不能把膀胱掛在身體外面,上廁所的時候就跟別人炫耀:hi,man,你瞅我的膀胱,看看我是怎麼尿的。
2. 電視機本身是一個黑盒子,隱藏了所有細節,但是一定會對外提供了一堆按鈕,這些按鈕也正是介面的概念,所以說,封裝並不是單純意義的隱藏!!!
3. 快門就是傻瓜相機為傻瓜們提供的方法,該方法將內部複雜的照相功能都隱藏起來了
提示:在程式語言裡,對外提供的介面(介面可理解為了一個入口),可以是函式,稱為介面函式,這與介面的概念還不一樣,介面代表一組介面函式的集合體。
#取款是功能,而這個功能有很多功能組成:插卡、密碼認證、輸入金額、列印賬單、取錢 #對使用者來說,只需要知道取款這個功能即可,其餘功能我們都可以隱藏起來,很明顯這麼做 #隔離了複雜度,同時也提升了安全性 class ATM: def __card(self): print('插卡') def __auth(self): print('使用者認證') def __input(self): print('輸入取款金額') def __print_bill(self): print('列印賬單') def __take_money(self): print('取款') def withdraw(self): self.__card() self.__auth() self.__input() self.__print_bill() self.__take_money() a=ATM() a.withdraw()隔離複雜度的例子
3: 瞭解
python並不會真的阻止你訪問私有的屬性,模組也遵循這種約定,如果模組名以單下劃線開頭,那麼from module import *時不能被匯入,但是你from module import _private_module依然是可以匯入的
其實很多時候你去呼叫一個模組的功能時會遇到單下劃線開頭的(socket._socket,sys._home,sys._clear_type_cache),這些都是私有的,原則上是供內部呼叫的,作為外部的你,一意孤行也是可以用的,只不過顯得稍微傻逼一點點
python要想與其他程式語言一樣,嚴格控制屬性的訪問許可權,只能藉助內建方法如__getattr__,詳見面向物件進階
四 特性(property)
什麼是特性property
property是一種特殊的屬性,訪問它時會執行一段功能(函式)然後返回值
例一:BMI指數(bmi是計算而來的,但很明顯它聽起來像是一個屬性而非方法,如果我們將其做成一個屬性,更便於理解)
成人的BMI數值: 過輕:低於18.5 正常:18.5-23.9 過重:24-27 肥胖:28-32 非常肥胖, 高於32 體質指數(BMI)=體重(kg)÷身高^2(m) EX:70kg÷(1.75×1.75)=22.86class People: def __init__(self,name,weight,height): self.name=name self.weight=weight self.height=height @property def bmi(self): return self.weight / (self.height**2) p1=People('egon',75,1.85) print(p1.bmi)View Code
例二:圓的周長和麵積
import math class Circle: def __init__(self,radius): #圓的半徑radius self.radius=radius @property def area(self): return math.pi * self.radius**2 #計算面積 @property def perimeter(self): return 2*math.pi*self.radius #計算周長 c=Circle(10) print(c.radius) print(c.area) #可以向訪問資料屬性一樣去訪問area,會觸發一個函式的執行,動態計算出一個值 print(c.perimeter) #同上 ''' 輸出結果: 314.1592653589793 62.83185307179586 '''View Code
#注意:此時的特性arear和perimeter不能被賦值 c.area=3 #為特性area賦值 ''' 丟擲異常: AttributeError: can't set attribute '''
為什麼要用property
將一個類的函式定義成特性以後,物件再去使用的時候obj.name,根本無法察覺自己的name是執行了一個函式然後計算出來的,這種特性的使用方式遵循了統一訪問的原則
除此之外,看下
ps:面向物件的封裝有三種方式:
【public】
這種其實就是不封裝,是對外公開的
【protected】
這種封裝方式對外不公開,但對朋友(friend)或者子類(形象的說法是“兒子”,但我不知道為什麼大家 不說“女兒”,就像“parent”本來是“父母”的意思,但中文都是叫“父類”)公開
【private】
這種封裝對誰都不公開
python並沒有在語法上把它們三個內建到自己的class機制中,在C++裡一般會將所有的所有的資料都設定為私有的,然後提供set和get方法(介面)去設定和獲取,在python中通過property方法可以實現
class Foo: def __init__(self,val): self.__NAME=val #將所有的資料屬性都隱藏起來 @property def name(self): return self.__NAME #obj.name訪問的是self.__NAME(這也是真實值的存放位置) @name.setter def name(self,value): if not isinstance(value,str): #在設定值之前進行型別檢查 raise TypeError('%s must be str' %value) self.__NAME=value #通過型別檢查後,將值value存放到真實的位置self.__NAME @name.deleter def name(self): raise TypeError('Can not delete') f=Foo('egon') print(f.name) # f.name=10 #丟擲異常'TypeError: 10 must be str' del f.name #丟擲異常'TypeError: Can not delete'
class Foo: def __init__(self,val): self.__NAME=val #將所有的資料屬性都隱藏起來 def getname(self): return self.__NAME #obj.name訪問的是self.__NAME(這也是真實值的存放位置) def setname(self,value): if not isinstance(value,str): #在設定值之前進行型別檢查 raise TypeError('%s must be str' %value) self.__NAME=value #通過型別檢查後,將值value存放到真實的位置self.__NAME def delname(self): raise TypeError('Can not delete') name=property(getname,setname,delname) #不如裝飾器的方式清晰瞭解:一種property的古老用法
五 封裝與擴充套件性
封裝在於明確區分內外,使得類實現者可以修改封裝內的東西而不影響外部呼叫者的程式碼;而外部使用用者只知道一個介面(函式),只要介面(函式)名、引數不變,使用者的程式碼永遠無需改變。這就提供一個良好的合作基礎——或者說,只要介面這個基礎約定不變,則程式碼改變不足為慮。
#類的設計者 class Room: def __init__(self,name,owner,width,length,high): self.name=name self.owner=owner self.__width=width self.__length=length self.__high=high def tell_area(self): #對外提供的介面,隱藏了內部的實現細節,此時我們想求的是面積 return self.__width * self.__length #使用者 >>> r1=Room('臥室','egon',20,20,20) >>> r1.tell_area() #使用者呼叫介面tell_area 400 #類的設計者,輕鬆的擴充套件了功能,而類的使用者完全不需要改變自己的程式碼 class Room: def __init__(self,name,owner,width,length,high): self.name=name self.owner=owner self.__width=width self.__length=length self.__high=high def tell_area(self): #對外提供的介面,隱藏內部實現,此時我們想求的是體積,內部邏輯變了,只需求修該下列一行就可以很簡答的實現,而且外部呼叫感知不到,仍然使用該方法,但是功能已經變了 return self.__width * self.__length * self.__high #對於仍然在使用tell_area介面的人來說,根本無需改動自己的程式碼,就可以用上新功能 >>> r1.tell_area() 8000View Code
相關推薦
Python學習第一天:面向物件之封裝
封裝是根據職責將屬性和方法 封裝到一個抽象的類中。 下面是我在學習中的練習案例: 1.小明愛跑步 class Person: def __init__(self,name,weight): self.name = name self.weight = w
java-13-面向物件之封裝
#面向物件之封裝 什麼是封裝 簡單說就是將屬性私有化,提供公有的方法。例如在下方 為什麼要封裝 通過封裝我們可以限制對屬性的訪問限制,同時新增程式的可維護性 this關鍵字是什麼意思 有
Java程式設計師從笨鳥到菜鳥之(三)面向物件之封裝,繼承,多型(下)
五:再談繼承 繼承是一種聯結類的層次模型,並且允許和鼓勵類的重用,它提供了一種明確表述共性的方法。物件的一個新類可以從現有的類中派生,這個過程稱為類繼承。新類繼承了原始類的特性,新類稱為原始類的派生類(子類),而原始類稱為新類的基類(父類)。派生類可以從它的基類那裡繼承方法和例項變數,並且類可以修改或增加
面向物件之封裝
一 引子 從封裝本身的意思去理解,封裝就好像是拿來一個麻袋,把小貓,小狗,小王八,還有alex一起裝進麻袋,然後把麻袋封上口子。照這種邏輯看,封裝=‘隱藏’,這種理解是相當片面的 二 先看如何隱藏 在python中用雙下劃線開頭的方式將屬性隱藏起來(設定成私有的) #其實這僅僅這是一種變形操作且僅
面向物件之封裝、繼承、重寫、過載、多型
1,封裝定義: 將物件的資料與操作資料的方法相結合,通過方法將物件的資料與實現細節保護起來,就稱之為封裝 例如: class Person(object): __age=0 def setter_age(self,age): if ag
面向物件 7 封裝之如何實現屬性的隱藏&封裝的意義&封裝可擴充套件性&property
封裝之如何實現屬性的隱藏 # class A: # __x=1 #'_A__x': 1 # # def __init__(self,name): # self.__name=name #self.__A__name=name
Python3 與 C# 面向物件之~封裝
關於__name__在模組呼叫的時候會詳細說,你可以先這樣理解:如果直接執行py檔案就執行,別人呼叫那麼你的main就不執行了 標準寫法: # 1.匯入的模組 # 2.class的定義 # 3.其他方法定義 def main(): pass if __name__ ==
Python學習之面向物件(封裝、繼承、多型)
面向物件 關於面向物件大家應該很熟知,即使說不出他的概念,但是至少記住他的三大特徵:封裝、繼承、多型。 封裝 所謂封裝,也就是把客觀事物封裝成抽象的類,並且類可以把自己的資料和方法只讓可信的類或者物件操作,對不可信的進行資訊隱藏。 類的定義
Python學習第二天:面向物件之繼承
繼承:實現程式碼的重用,相同的程式碼不需要重複的編寫。 1.繼承的語法 ''' 專業術語: Dog類是Animal類的子類,Animal類是Dog類的父類,Dog類從Animal類繼承 Dog類是Animal類的派生類,Animal類是Dog類的基類,Dog類從Animal類派生
PHP面向物件之介面
PHP與大多數面向物件程式語言一樣,不支援多重繼承。也就是說每個類 只能繼承一個父類。 為了解決此問題,PHP引入了介面。 介面的思想是指定了一個實現該介面的類必須實現的一系列方法。 介面是一種特殊的抽象類,抽象類是一種特殊的類,所以介面也是一種特殊的類。 為什
Python--day25--面向物件之多型
多型(Python天生支援多型) 多型指的是一類事物有多種形態 動物有多種形態:人,狗,豬 1 import abc 2 class Animal(metaclass=abc.ABCMeta): #同一類事物:動物 3 @abc.abstractmethod 4 def t
JS面向物件之物件屬性的屬性
ECMAScript的定義中,物件的屬性有兩種,一種是資料屬性,另一種是訪問器屬性 我們知道JS中有基本資料型別和物件,物件中的屬性也無外乎是這些型別,可是作為物件的屬性,這些屬性本身卻也多了一些特殊的屬性,而且屬性種類的不同也會使屬性的屬性有些許的差別。下面就來聊聊物件屬性的兩種類別。
JS 面向物件之原型鏈
物件的原型鏈 只要是物件就有原型 原型也是物件 只要是物件就有原型, 並且原型也是物件, 因此只要定義了一個物件, 那麼就可以找到他的原型, 如此反覆, 就可以構成一個物件的序列, 這個結構就被成為原型鏈 原型鏈到哪裡是一個頭? 一個預設的原型鏈結構是什麼樣子的?(當前物件-
JavaScript 面向物件之二 —— 函式上下文(call() 和 apply())
本系列文章根據《愛前端邵山歡老師深入淺出的js面向物件》視訊整理歸納 call() 和 apply() 這兩個都是函式的方法,只有函式能夠通過點方法呼叫call()、apply(),表示用指定的上下文執行這個函式。 如下,定義一個函式 fun,當 fun 函式裡
JavaScript 面向物件之二 —— 函式上下文(this的指向)
本系列文章根據《愛前端邵山歡老師深入淺出的js面向物件》視訊整理歸納 函式上下文 在 JavaScript 中,函式的上下文是有規律可循的,基本可以分為以下幾項: 規律一:函式用圓括號呼叫,函式上下文是 window 物件。 如下,函式 function f
Java面對物件之封裝
學習到了封裝,也希望這篇文章能個給後面學習java的人一點幫助. 首先我們用實際問題先感受一下封裝的概念. 用人來舉例,人的年齡是有限的,0-120左右 我們在社交網站填寫個人資訊的時候,也會有年齡這一項,那麼這個年齡允許填寫負數或者很大的數嗎? 實際上是不允許的.
week6:面向物件之成員修飾符,特殊成員,異常處理,發射,單例
一、成員修飾符 共有成員 私有成員, __欄位名 - 無法直接訪問,只能間接訪問 class Foo: def __init__(self, name, age): self.name
day19--面向物件之約束
一、上次內容回顧 1、反射 1、hasattr(物件,屬性(字串)) 2、getattr(物件,屬性(字串)) 3、setattr(物件,屬性,值) 4、delattr(物件,屬性) 2、issubclass ,type , isinstance
JAVA---面向物件之從生活中抽取例項物件
public class Car{ private String brand; private int num; private String colour; private int seats; //建構函式 public Car(String brand){
Java面向物件之多型(向上轉型與向下轉型)
多型,大概每個人都知道。但是,又有幾個人真的理解什麼是多型、多型有哪些細節呢?如果你看到這篇文章的名字,腦海中對多型沒有一個清晰的概念,不妨點進來看看,也許會有收穫。 什麼是多型 簡單的理解多型 多型,簡而言之就是同一個行為具有多個不同表現形式或形態的能力。比如說,有一杯水,我不知道