第十五章:封裝

01. 面相對象-類的特殊方法

01.類的特殊方法
目前,對於person類來說 name屬性是必須的,並且每一個對象當中的name屬性基本上是不同的

在類中我們可以定義一些特殊的方法(魔術方法)
這些特殊方法它的格式例如 以__開頭__結尾的方法
特殊的方法不需要我們自己調用

學習特殊方法
1.特殊方法什麼時候調用
2.特殊方法有什麼作用?

創建對象的流程
p1 = person
1.創建一個變量
2.在內存中創建一個新的對象
3.執行類中的代碼塊中的代碼(只在類定義的時候執行一次)
4.init(self)方法執行

定義一個類的基本結構
‘’’
class 類名([父類])
公共屬性

# 對象的初始化方法
def _init_(self):
    ....

def method1(self.......):
def method2(self.......):
    ....

‘’’

class person():
    # print('person代碼塊中的代碼')

    # name = '葫蘆娃'
    # 特殊的方法不需要我們自己調用,不要去嘗試調用特殊方法
    # 特殊的方法會在特殊的時候自己調用(一般就是解析器調用)

    def __init__(self,name):
        # print('init方法執行了')
        # print(self)
        # 通過self向新創建的對象中初始化屬性
        # self.name = '葫蘆娃'
        self.name = name

    def speak(self):
        print('大家好,我是%s'%self.name)

p1 = person('葫蘆娃')
p2 = person('鋼鐵俠')
p3 = person('蜘蛛俠')
p4 = person('戰狼')

print(p1.name)
print(p2.name)
print(p3.name)
print(p4.name)

p1.speak()
p2.speak()
p3.speak()
p4.speak()

# p1.__init__()

# # 手動添加對象的name屬性
# p1.name = '葫蘆娃'

# p1.speak()

# p2 = person()
#
# p2.name = '鋼鐵俠'

# p2.speak()
#

#
# p3 = person()
#
# p3.name = '蜘蛛俠'

# p3.speak()

02. - 面向對象 - 練習

‘’’
屬性:
name
color
方法:
run()
laba()

‘’’

‘’’
關於錯誤:object() takes no parameters

先說結論:

如果你出現了這個報錯,請檢查你的__init__函數名或者其定義有沒有寫錯。

注意:下劃線左右是兩個;中間的英文字母是四位,請一一對應。

--------------------------------------如果你有興趣可以看看下面的分析過程------------------------------------

一、現象

先貼上自己出錯的代碼:

class Perceptron(object):
def int(self, eta=0.01, n_iter=10):
self.eta = eta
self.n_iter = n_iter
pass
pass

ppn=Perceptron(eta=0.1,n_iter=10)
報錯如下:
Traceback (most recent call last):
File “D:/PyCharm/Neutron/Perceptron.py”, line 13, in
ppn=Perceptron(eta=0.1,n_iter=10)
TypeError: object() takes no parameters

爲什麼會這樣呢?

二、分析

”object() takes no parameters”這句話的意思是:object()不需要傳進參數。

筆者瞬間就感到納悶了,爲什麼不需要傳入參數?我不是定義了初始化函數嗎?這初始化函數裏面有倆參數啊,爲什麼不能傳遞呢?(這位朋友,你戲真多)

最可能的解釋是:

在實例化一個對象的時候,使用 類名+(參數)並沒有成功調用到這個初始化參數,編譯器默認調用類名+( ) ,這是一個無參的初始化函數,自然就不需要傳進參數了。

如果嘗試調用自己定義的初始化函數的方法沒有錯的話,那麼之所以不能成功調用初始化函數,錯誤就在於初始化函數本身!

回去檢查一下我寫的這個初始化函數,猛地發現我把__init__寫成了__int__,難怪啊!

改成正確的函數名以後,就可以成功調用,不會報錯了~

‘’’

class car():

    # __init__函數 下劃線左右是兩個,中間的英文字母是四位:init
    def __init__(self,name,color):

        self.name = name
        self.color = color

    def run(self):
        print('汽車開始跑了~~~~~')

    def laba(self):
        print('%s 滴滴滴滴'%self.name)

# 目前,我們是可以通過  對象.屬性  的方式來修改屬性的值,這種方式就可以導致對象中的屬性可以隨意修改
c = car('大奔','白色')
c.name = '法拉利'
c.color = '黑不溜秋'

print(c.name,c.color)
c.run()
c.laba()

我們想要增加數據的安全性,那麼我們要做到兩點
1.屬性不能隨意修改
2.屬性不能改爲任意值

03. - 面向對象 - 封裝一

封裝是面向對象的三大特徵之一
封裝是指隱藏對象中一些不希望被外部訪問到的屬性和方法

如何隱藏對象啊行中的屬性
將對象的屬性名修改成外部不知道的名字

如何獲取(修改)對象當中的屬性
需要提供一個getter和setter方法使外部可以訪問到屬性

‘’’
使用封裝確實增加了類定義的複雜程度,但是它也確保了我們數據的安全性
1.隱藏了屬性名,使用者無法隨意的修改對象的屬性
2.增加了getter和setter方法之後,很好的控制了屬性是否是隻讀的
如果你希望屬性是隻讀的,則可以直接去調用getter方法
如果希望屬性不能被外部訪問,則可以去調用getter方法
3.使用setter方法設置屬性,可以增加數據的驗證,確保數據的正確性
4.使用getter方法獲取屬性,使用setter方法設置屬性
我們可以在讀取屬性和修改屬性的時候做一些其他的操作
5.setter 和 getter方法也可以表示一些計算的屬性
‘’’

hidden在這裏是隱藏的意思

class dog():

    def __init__(self,name,age):

        self.hidden_name = name
        self.hidden_age = age
    def speak(self):

        print('大家好,我是%s'%self.hidden_name)

# get_name(self) 獲取對象中指定的屬性(get_屬性名)
    def get_name(self):
        # get_name(self)用來獲取對象的name屬性

        print('get_name執行了')
        return self.hidden_name

# set_name(self,   ): 用來設置對象中指定的屬性值(set_屬性名)
    def set_name(self,name):

        print('set_name執行了')
        self.hidden_name = name

    def get_age(self):
        return self.hidden_age

    def set_age(self,age):
        if age > 0:
            self.hidden_age = age

d = dog('二哈',5)

# d.hidden_name = '德國牧羊犬'    #  這個不是封裝,而是暴力修改

# print(d.get_name())

# 調用setter來修改name的屬性值
d.set_name('德牧')
d.set_age(8)
# print(d.get_name())
print(d.get_age())

# d.speak()

>>>
set_name執行了
8

04.- 面向對象 - 封裝二

練習:計算長方形的面積

class Rect():

    def __init__(self,width,height):

        self.hidden_width = width
        self.hidden_height = height

    def get_width(self):
        return self.hidden_width

    def get_height(self):
        return self.hidden_height


    def set_width(self,width):
        self.hidden_width = width

    def set_height(self,height):
        self.hidden_height = height

    def get_area(self):
        return self.hidden_width * self.hidden_height

r = Rect(2,3)

r.set_width(5)
r.set_height(10)

print(r.get_area())
>>>
>50

如果我希望封裝的更加徹底,外部訪問不到
可以爲對象的屬性使用雙下劃線 __xxx
__雙下劃線開頭的屬性,是對象的隱藏屬性,隱藏屬性只能在類的內部訪問,無法通過對象來訪問
__name --> _person__name 實際上是將名字改爲 _類名__屬性名
一般我們會將一些私有屬性(不希望被外部訪問的屬性) 以_xx 開頭(一個下劃線就可以了)

class person():

    def __init__(self,name):
        # self.hidden_name = name
        self._name = name

    def get_name(self):
        # return self.hidden_name
        return self._name

    def set_name(self,name):
        # self.hidden_name = name
        self._name = name

p = person('葫蘆娃')
# p.set_name('鋼鐵俠')
# p.hidden _name = '蜘蛛俠'
# print(p._person__name)
# p._person__name = '蜘蛛俠'
# p.__name = '蜘蛛俠'
# print(p.get_name())
print(p._name)

self._name 這樣寫的好處是什麼?
1.一個下劃線要比兩個下劃線要容易書寫
2.外部依然可以讀取
3.我以這種方式來寫就是告訴你不希望你來修改

05. - 面向對象 - property()裝飾器

property(), 是用來將一個get()方法,轉換爲一個屬性

class person():

    def __init__(self,name):
        self._name = name

    @property
    def name(self):
        print('get方法執行了')
        return self._name

    # 設置setter方法的裝飾器 @屬性名.setter
    @name.setter
    def name(self,name):
        print('set方法執行了')
        self._name = name

p = person('葫蘆娃')

# print(p.name())
p.name = '鋼鐵俠'
print(p.name)
>>>
set方法執行了
get方法執行了
鋼鐵俠
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章