Python 面向對象編程OOP (一) 類,對象,屬性,訪問權限

Python面向對象編程之旅

OOP編程是什麼

你們好,做爲小白,最近學習了不少Python OOP編程的知識,由於腦容量有限,特此一一按照學習順序記錄下來,若是哪裏有錯誤,還請大神儘快指出,以避免誤導他人。。。java

首先讓咱們簡單瞭解一下何爲面向對象編程:python

把一組數據結構和處理它們的方法組成對象(object),把相同行爲的對象概括爲類(class),經過類的封裝(encapsulation)隱藏內部細節,經過繼承(inheritance)實現類的特化(specialization)和泛化(generalization),經過多態(polymorphism)實現基於對象類型的動態分派。

這樣一說貌似有些複雜,簡單來看的話能夠參考下面的解釋:c++

常見概念一覽

概念 解釋
類(Class) 用來描述具備相同的屬性和方法的對象的集合。它定義了該集合中每一個對象所共有的屬性和方法。對象是類的實例
類變量 類變量在整個實例化的對象中是公用的。類變量定義在類中且在函數體以外。類變量一般不做爲實例變量使用
數據成員 類變量或者實例變量, 用於處理類及其實例對象的相關的數據
方法重寫 若是從父類繼承的方法不能知足子類的需求,能夠對其進行改寫,這個過程叫方法的覆蓋(override),也稱爲方法的重寫
局部變量 定義在方法中的變量,只做用於當前實例的類
實例變量 在類的聲明中,屬性是用變量來表示的。這種變量就稱爲實例變量,是在類聲明的內部可是在類的其餘成員方法以外聲明的
繼承 即一個派生類(derived class)繼承基類(base class)的字段和方法。繼承也容許把一個派生類的對象做爲一個基類對象對待。例如,有這樣一個設計:一個Dog類型的對象派生自Animal類,這是模擬"是一個(is-a)"關係(例圖,Dog是一個Animal)
實例化 建立一個類的實例,類的具體對象
方法 類中定義的函數
對象 經過類定義的數據結構實例。對象包括兩個數據成員(類變量和實例變量)和方法

定義一個類

下面讓咱們簡單定義一個汽車類:git

class Car:
    def __init__(self, color, model, year):

        self.color = color
        self.model = model
        self.year = year

這裏咱們建立了一個汽車類Car,它有三個公共屬性,分別是color(顏色),model(型號),year(生產年份)github

建立實例對象,訪問屬性

如今讓咱們新建一個對象my_car:編程

my_car = Car("yellow", "beetle", 1967)

查看一下my_car的屬性數據結構

print(f" My {my_car.color} car {my_car.model} is made in {my_car.year}")
# My yellow car beetle is made in 1967

添加新屬性

咱們想要給my_car添加一個新屬性wheelsssh

my_car.wheels = 5
print(f"Wheels: {my_car.wheels}")
# Wheels: 5

使用dir(my_car)可讓咱們確認一下屬性是否存在:ide

dir(my_car)

Out:
['__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__',
 'color',
 'model',
 'wheels',     <====已經添加成功啦
 'year']

類變量,修改類變量的值

在Python中,咱們在類外聲明一個類變量,下面讓咱們修改一下Car類:函數

class Car:
    wheels = 0
    def __init__(self, color, model, year):
        self.color = color
        self.model = model
        self.year = year

這樣的話,咱們在調用wheels這個變量時,能夠經過實例,或者直接調用Car.wheels:

my_car = Car("yellow", "beetle", 1967)
print(f"My car is {my_car.color}")
print(f"It has {Car.wheels} wheels")
print(f"It has {my_car.wheels} wheels")

Out:
My car is yellow
It has 0 wheels
It has 0 wheels

這裏須要注意一下,若是想要經過my_car.wheels =xxx來修改wheels的值,不會真正修改類變量wheels的值,咱們來看一個具體的例子:

my_car = Car("yellow", "Beetle", "1966")
my_other_car = Car("red", "corvette", "1999")

print(f"My car is {my_car.color}")
print(f"It has {my_car.wheels} wheels")


print(f"My other car is {my_other_car.color}")
print(f"It has {my_other_car.wheels} wheels")

Out:
My car is yellow
It has 0 wheels
My other car is red
It has 0 wheels

咱們首先建立兩個實例my_car 和my_other_car ,默認的wheels=0,下面咱們首先直接經過Car這個類來修改類變量的值:

# Change the class variable value

Car.wheels = 4

print(f"My car has {my_car.wheels} wheels")
print(f"My other car has {my_other_car.wheels} wheels")

Out:
My car has 4 wheels
My other car has 4 wheels

能夠看到這樣修改的話,Car類擁有的全部實例中的wheels值會被所有修改,若是咱們經過my_other_car 來修改呢?

# Change the instance variable value for my_car

my_car.wheels = 5
print(f"My car has {my_car.wheels} wheels")
print(f"My other car has {my_other_car.wheels} wheels")

Out:
My car has 5 wheels
My other car has 4 wheels

如今你們能夠發現區別了,僅僅是修改了my_car中wheels的值,對類自己不會形成影響

私有和公有屬性

在Python中的全部屬性都是public,可能有c++和java的同窗以爲神奇,其實python最初規定了一種特殊的命名方式來區分public仍是private,那就是下劃線_

我仍是拿同樣的例子說明:

class Car:
    wheels = 0
    def __init__(self, color, model, year):
        self.color = color
        self.model = model
        self.year = year
        self._cupholders = 6

my_car = Car("yellow", "Beetle", "1969")
print(f"It was built in {my_car.year}")

Out:
It was built in 1969

這裏Car類中的杯託 _cupholders就是「私有「屬性,爲何我這裏加上了引號,是由於Python只是名義上規定這種寫法,可是在實際訪問上沒啥卵用,依然能夠直接用._cupholders來訪問:

my_car.year = 1966
print(f"It was built in {my_car.year}")
print(f"It has {my_car._cupholders} cupholders.")

Out:
It was built in 1966
It has 6 cupholders.

後來Python決定使用雙下劃線__來替換單下劃線,這樣能夠最大程度避免「意外訪問「,然而仍是沒有卵用,再來展現一下新方案:

class Car:
    wheels = 0
    def __init__(self, color, model, year):
        self.color = color
        self.model = model
        self.year = year
        self.__cupholders = 6

其實某種程度上,這回效果仍是很明顯的,若是咱們還像剛纔同樣嘗試調用my_car.cupholders 會報錯:

my_car = Car("yellow", "Beetle", "1969")
print(f"It was built in {my_car.year}")
print(f"It has {my_car.__cupholders} cupholders.")


Out:
It was built in 1969

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-108-1efe56f0c054> in <module>
 1 my_car = Car("yellow", "Beetle", "1969")
 2 print(f"It was built in {my_car.year}")
----> 3  print(f"It has {my_car.__cupholders} cupholders.")

AttributeError: 'Car' object has no attribute '__cupholders'

這個錯誤頗有意思,爲何會說cupholders這個變量不存在呢 ? 由於當Python看到__ 時,會自動在cupholders前面補上一個下劃線_和所屬類名,也就是說,這裏咱們嘗試用my_car.__cupholders 來調用時,Python默認的正確寫法是
my_car._Car__cupholders,如今再試一下:

print(f"It has {my_car._Car__cupholders} cupholders")
Out: It has 6 cupholders

看見沒,依然沒攔住。。。。
不過我我的認爲這種規定公有私有變量的方式也是好處多多,這裏就仁者見仁,智者見智了~

訪問權限管理

就像剛剛提到的,Python全部的東西都是公有的,咱們能夠隨意的新增,修改,甚至刪除變量:

my_car = Car("yellow", "beetle", 1969)
print(f"My car was built in {my_car.year}")

my_car.year = 2003
print(f"It was built in {my_car.year}")

del my_car.year
print(f"It was built in {my_car.year}")

Out:
My car was built in 1969
It was built in 2003

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-110-46914b0bae82> in <module>
 6 
 7 del my_car.year
----> 8  print(f"It was built in {my_car.year}")

AttributeError: 'Car' object has no attribute 'year'

那咱們如何才能控制屬性的訪問權限呢?Python給出的答案是裝飾器 @property,這個相似於Java中的setter和getter,如今咱們試試:

class Car:
    def __init__(self, color, model, year):
        self.color = color
        self.model = model
        self.year = year
        self._voltage = 12

    @property
    def voltage(self):
        return self._voltage

    @voltage.setter
    def voltage(self, volts):
        print("Warning: this can cause problems!")
        self._voltage = volts

    @voltage.deleter
    def voltage(self):
        print("Warning: the radio will stop working!")
        del self._voltage

咱們新增了voltage(電壓)這個屬性,並用property來控制外部的訪問權限,這裏咱們定義了三個方法,利用setter方法能夠改變voltage的值,利用getter方法來訪問,利用deleter方法實現刪除,接下來讓咱們新建實例來看看propert是如何工做的:

my_car = Car("yellow", "beetle", 1969)
print(f"My car uses {my_car.voltage} volts")

my_car.voltage = 6
print(f"My car now uses {my_car.voltage} volts")

del my_car.voltage

Out:
My car uses 12 volts
Warning: this can cause problems!
My car now uses 6 volts
Warning: the radio will stop working!

能夠發現,咱們這裏直接使用.voltage 而不是._voltage,這樣就告訴python去使用property裝飾的方法,咱們能夠經過使用@.setter and @.deleter 使屬性變爲read-only(只讀),從而保護voltage不會被隨意修改和刪除

總結

今天主要總結了OOP編程中的類,對象,屬性,公有私有屬性,訪問權限這些基礎概念,下一篇文章會進一步深刻,若是本文有哪些語言使用不當,但願你們能夠指出,讓咱們一塊兒進步!

我以前的一些文章已經放到了Github上,若是感興趣的朋友能夠去看看,連接以下:

相關文章
相關標籤/搜索