1. 程式人生 > >面向物件之封裝

面向物件之封裝

一 引子

從封裝本身的意思去理解,封裝就好像是拿來一個麻袋,把小貓,小狗,小王八,還有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):
...     
def 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
View Code

三 封裝不是單純意義的隱藏

封裝的真諦在於明確地區分內外,封裝的屬性可以直接在內部使用,而不能被外部直接使用,然而定義屬性的目的終歸是要用,外部要想用類隱藏的屬性,需要我們為其開闢介面,讓外部能夠間接地用到我們隱藏起來的屬性,那這麼做的意義何在???

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.86
class 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()
8000
View 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面向物件多型(向上轉型與向下轉型)

多型,大概每個人都知道。但是,又有幾個人真的理解什麼是多型、多型有哪些細節呢?如果你看到這篇文章的名字,腦海中對多型沒有一個清晰的概念,不妨點進來看看,也許會有收穫。 什麼是多型 簡單的理解多型 多型,簡而言之就是同一個行為具有多個不同表現形式或形態的能力。比如說,有一杯水,我不知道