python:面向對象程序設計及property裝飾器

 

Python面向對象設計特色

  • 具備面向對象的全部特徵:封裝、繼承、多態;
  • 全部類的父類爲object;
  • 子類能夠對父類的任何方法均可能進行重寫;
  • Python中沒有提供重載和訪問控制,可是屬性能夠用特殊名進行訪問控制

定義類

類定義格式以下:java

class A:
    pass
class B(A):
    pass
屬性和方法

在java和C++中,分別提供了this引用和this指針,表示當前的調用對象或指針,在Python中,則提供了相似的self參數,在對方法進行調用時,會自動提供self參數,可是必須在參數列表中包含該參數,所以,方法的定義格式以下:python

class A:
    def func(self):
        print("我是普通方法")

有了self參數,類的屬性就是經過self參數來存取,如:安全

class A:
    def func(self,name,age):
        self.name = name
        self.age = age
        print("我是普通方法")    

完整的類定義以下:函數

class A:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def func(self):
        print(666)
    def __eq__(self, other):
        return self.name == other.name and self.age == other.age
    def __repr__(self):
        return "Animal('{0}',{1}".format(self.name,self.age)
    def __str__(self):
        return "Animal({0},{1}".format(self.name,self.age)

a1 = A('alex',1000)
print(a1.name) #'alex"
print(a1.func()) # 666  None
print(a1.__eq__(a1)) # True
print(a1.__str__()) #Animal(alex,1000
print(a1.__repr__()) #Animal('alex',1000

其中以_開頭和結尾的方法都來自於基類object,當定義好類後,就能夠了導入人類所在模塊後使用了,this

如:spa

import Module_A
dog = Module_A
dog.eat()

若是想要定義一個私有的屬性,則在屬性前面加上__就變成私有的了設計

如:指針

class A:
    def __init__(self,name='alex',age=1000):
        self.__name=name
        self.age=age
if __name__ == "__main__":
    dog = A()
    print(dog.__name)
    #AttributeError: 'A' object has no attribute '__name'
基本特殊方法

__new()____init()__方法: 
和其餘語言不一樣,Python中要建立一個對象,當調用dog = Module_Animal.Animal("Dog")時,執行了兩個步驟:code

  • 第一步,使用__new__()方法建立該對象;
  • 第二步,使用__init__()方法對其進行初始化;

在建立類時,應該重寫__init__()方法進行初始化,__new__()沒必要須進行重寫。在建立對象時若是發現沒有提供,則自動調用object.__new__()。 
若是重寫了__init__()方法而且想要調用基類該方法,則能夠經過super()方法:orm

def __init__(self):
    super().__init__()

__eq__(self, other)__ne__(self, other)方法: 
這兩個方法用於兩個對象之間進行比較,對於__eq__(),若是self和other相等,則返回True;對於__ne__(),若是self和other不等,則返回True,如:

dog = Animal("Dog")
dog.eat()
cat = Animal("cat")
cat.eat()
print(dog == cat)   # return False
isEqual = dog.__eq__(cat)
isNotEqual = dog.__ne__(cat)
print(isEqual) # return False
print(isNotEqual)  # return True

 

  • 默認狀況下,全部的自定義類都支持==進行比較,但老是返回False(除非dog == dog),所以能夠經過__eq__()來實現比較操做。
  • 若是提供了__eq__()方法但沒有提供__ne__()方法,Python會自動提供__ne__()!=操做符;若是沒有提供__eq__(),調用結果爲NotImplemented。
  • 默認狀況下,自定義類的全部實例都是可哈希運算的,能夠調用__hash__()方法,也能夠做爲字典的鍵,可是若是重寫了__eq__()方法,則該類的實例就不是可哈希運算的了。
  • 應避免非同類型間進行比較,可用內置函數isInstance(obj,Obj)處理,第一個參數爲對象,第二個爲類型,如:
def __eq__(self, other):
    if not isinstance(other,Animal):
        return NotImplementedError
    return self.name == other.name and self.age == other.age

 

  • 對於比較操做符,Python中都提供了特殊的方法: 
    • __le__(self,other):self <= other,返回Ture
    • __lt__(self,other):self < other,返回Ture
    • __ge__(self,other):self >= other,返回Ture
    • __gt__(self,other):self > other,返回Ture
    • __ne__(self,other):self != other,返回Ture
    • __eq__(self,other):self == other,返回Ture

__repr__()__str__()方法: 
調用內置repr()函數時,會調用給定對象的__repr__()方法,該方法用於返回一個特殊的字符串,該字符串可經過內置eval()函數生成一個Python對象,如:

def __repr__(self):
    return "Animal('{0}',{1})".format(self.name,self.age)
if __name__ == "__main__":
    cat = Animal("Cat")
    ani = eval(repr(cat))  # return Animal('Cat',0)
    print(ani)  # Animal(Cat,0)

 

 

調用內置函數str()時,會調用給定對象的__str__()方法,該方法也會返回一個字符串,和__repr__()的區別主要是該方法產生的字符串主要是便於理解,而不是爲了傳遞給eval()函數。 
如:

def __str__(self):
    return "Animal({0},{1})".format(self.name, self.age)
    print(str(ani))  # Animal(Cat,0)

 

  • 若是實現了__repr__()而沒有實現__str__()時,則在調用str()、print(obj)時也會執行__repr__()方法。 
    __hash__()方法: 
    默認狀況下,全部的類都是能夠哈希運算的,可是若是類實現了__eq__()方法,那麼則不可進行哈希運算,經過提供該方法能夠使類能狗進行哈希運算,實現以下:
def __hash__(self):
    return hash(id(self))

 

 

內置hash()方法根據獨一無二的ID來計算哈希值,內置id()方法返回一個獨一無二的整數

繼承和多態

Python中定義一個類繼承另外一個類格式以下:

class Subclass(A):
    pass

 

子類能夠對父類的方法進行重寫,若是子類想調用父類的方法,能夠使用super()進行調用,如:

class Animal:
    def __init__(self,name="Animal",age=0):
        self.__name = name
        self.age = age

    def eat(self):
        print('Animal is eating')

    def shout(self):
        print("Animal is shouting")


class Dog(Animal):
    def __init__(self,name="Dog",age=0):
        # 調用Animal的__init__()方法
        super().__init__()
        self.name = name
        self.age = age

    def shout(self):
        print("Dog is Shouting")

    def eat(self):
        print("Dog is eating")

class Cat(Animal):
    def __init__(self,name="Cat"):
        self.name = name

    def shout(self):
        print("Cat is shouting")

    def eat(self):
        print("Cat is eating")

 

在java等面嚮對象語言中,多態是指子類的引用指向父類的實例,可是在Python中,可能不能用這種方式來理解,由於Python中定義一個變量或對象並不須要聲明數據類型,所以對於Python而言,其多態的表現形式就是:若是子類對父類方法進行了覆蓋,則子類對象調用該方法時,會調用子類對方法的實現。如:

if __name__ == "__main__":

    ani = Animal()
    dog = Dog()
    cat = Cat()
    ani.eat()  # Animal is eating
    dog.eat()  # Dog is eating
    cat.eat()  # Cat is eating

 

 

python提供了isinstance(o,Obj)方法,能夠判斷對象o是不是Obj類型,所以,在使用多態時,能夠這樣使用:

# 定義一個函數
def animal_eat(animal):
    animal.eat()

if isinstance(dog,Animal):
    animal_eat(dog) # Dog is eating
if isinstance(cat,Animal):
    animal_eat(cat) # Cat is eating

 

 

property裝飾器

在面向對象的設計中,經常要將屬性進行封裝,提供setter/getter方法對屬性進行操做,Python中也能夠提供setter/getter進行對屬性的封裝,從而保證數據的安全性,可是並不推薦使用,由於有更優的方式能夠屬性的安全性,下面逐步進行分析。 
如今定義一個類:

import math

class Circle:

    def __init__(self, radius, x=0, y=0):
        self.radius = radius

    def area(self):
        return math.pi * (self.radius ** 2)


if __name__ == "__main__":

    c1 = Circle(2)
    c1.radius = -3
    print(c1.area())  
    print(c1.radius)  # -3

 

 

能夠看到,屬性radius有可能被設置爲不合理的值,所以,能夠將radius設置爲私有屬性,同時提供setter/getter方法對其進行設置,修改該類實現以下:

class Circle:

    def __init__(self, radius, x=0, y=0):
        if radius <= 0: 
            self.__radius = 1
        self.__radius = radius

    def set_radius(self, radius):
        assert radius >= 0, "radius can't be zero"
        self.__radius = radius

    def get_radius(self):
        return self.__radius


if __name__ == "__main__":

    c1 = Circle(2)
    c1.set_radius(-3)  # AssertionError: radius can't be zero
    print(c1.area())
    print(c1.get_radius())  # 2

 

 

經過setter/getter能夠將屬性封裝起來,能夠保證屬性的安全性,可是這種方式比較繁瑣,在Python中,更傾向於使用Property裝飾器,使用方式以下:

# class A:
#     def __init__(self,name,age):
#         self.name = name
#         self.age = age
#     def func(self):
#         print(666)
#     def __eq__(self, other):
#         return self.name == other.name and self.age == other.age
#     def __repr__(self):
#         return "Animal('{0}',{1}".format(self.name,self.age)
#     def __str__(self):
#         return "Animal({0},{1}".format(self.name,self.age)
#
# a1 = A('alex',1000)
# print(a1.name) #'alex"
# print(a1.func()) # 666  None
# print(a1.__eq__(a1)) # True
# print(a1.__str__()) #Animal(alex,1000
# print(a1.__repr__()) #Animal('alex',1000


class A:
    def __init__(self,name='alex',age=1000):
        self.__name=name
        self.age=age
if __name__ == "__main__":
    dog = A()
    print(dog.__name)
    #AttributeError: 'A' object has no attribute '__name'

 

 

Property裝飾器是一個函數,該函數以一個方法做爲參數,並返回修飾後的版本。可是一般並不使用該方法,而是經過@符號來標記,如上例所示。property()是一個內置函數,至多能夠接受四個參數:get參數、set參數、delete參數、docstring參數。@property至關於property(get)。 
在上例中,建立了一個__radius私有屬性,而後經過property裝飾器進行其getter、setter的設置。須要注意:

  • getter和setter有一樣的名稱,如def radius(self);
  • 當使用@property建立特性後,每一個建立的特性都包含getter、setter、deleter、docstring等屬性,而且都是可用的。
  • getter爲@property設置的方法,其餘屬性由python設置。

當建立radius裝飾器後,就能夠經過radius.setter和radius.getter獲取和設置__radius屬性了,同時保證了私有屬性的安全性,如:

c1.radius = 0  # AssertionError: radius can't be zero
c1.radius = 3  # 調用radius(self,radius),至關於radius.setter
print(c1.radius)  # 2  調用radius.setter
相關文章
相關標籤/搜索