1. 程式人生 > >python基礎-abstractmethod、__屬性、property、setter、deleter、classmethod、staticmethod

python基礎-abstractmethod、__屬性、property、setter、deleter、classmethod、staticmethod

python中的介面概念

在python中根本就沒有一個叫做interface的關鍵字,如下的程式碼只是看起來像介面,其實並沒有起到介面的作用

利用NotImplementedError


class Payment:
    def pay(self):
        raise NotImplementedError

class ApplePay(Payment):
    def zhifu(self,money):
        print("ApplePay zhifu %d" % money)

#必須實現pay方法,否則報錯NotImplementedError
# def pay(self): # print("ApplePay pay") app = ApplePay() app.zhifu(200) #ApplePay必須實現,才呼叫不報錯 app.pay()

輸出如下:

E:\python\python_sdk\python.exe E:/python/py_pro/python.py
Traceback (most recent call last):
  File "E:/python/py_pro/python.py", line 51, in <module>
    app.pay()
  File "E:/python/py_pro/python.py"
, line 38, in pay raise NotImplementedError NotImplementedError ApplePay zhifu 200 Process finished with exit code 1

所以我們通過raise NotImplementedError方式,強制其子類,實現方法,才能不報錯,將上面程式碼註釋去掉,然後就可以順利輸出如下的正確資訊:

E:\python\python_sdk\python.exe E:/python/py_pro/python.py
ApplePay zhifu 200
ApplePay pay

利用abstractmethod

注意事項:
子類必須全部實現重寫父類的abstractmethod方法
非abstractmethod方法可以不實現重寫
帶abstractmethod方法的類不能例項化

#介面類
from abc import ABCMeta,abstractmethod
class Payment(metaclass=ABCMeta):
    @abstractmethod
    def pay(self,money):
        pass

    @abstractmethod
    def get(self, money):
        print("Payment get%d" % money)

    def total(self,money):
        print("Payment total %d" % money)

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

class AppPay(Payment):
    def pay(self,money):
        print("AppPay pay %d"%money)

    def get(self,money):
        print("AppPay get %d" % money)

app = AppPay("safly")
app.pay(100)
app.get(200)
app.total(400)
# 不能例項化
# TypeError: Can't instantiate abstract class Payment
# with abstract methods get, pay
# a = Payment("safly")

輸出如下:

E:\python\python_sdk\python.exe E:/python/py_pro/python.py
<__main__.AppPay object at 0x01201E30>
AppPay pay 100
AppPay get 200
Payment total 400

抽象類

與java一樣,python也有抽象類的概念但是同樣需要藉助模組實現,抽象類是一個特殊的類,它的特殊之處在於只能被繼承,不能被例項化

from abc import ABCMeta,abstractmethod
class Base(metaclass=ABCMeta):
    def __init__(self,fName):
        self.fName = fName;

    @abstractmethod
    def open(self):pass

class File(Base):
    def open(self):
        print("file open")

file = File("safly")
file.open()

輸出如下:

E:\python\python_sdk\python.exe E:/python/py_pro/python.py
file open

多型概念

在面向物件方法中一般是這樣表述多型性:
向不同的物件傳送同一條訊息(!!!obj.func():是呼叫了obj的方法func,又稱為向obj傳送了一條訊息func),不同的物件在接收時會產生不同的行為(即方法)。
也就是說,每個物件可以用自己的方式去響應共同的訊息。所謂訊息,就是呼叫函式,不同的行為就是指不同的實現,即執行不同的函式。

比如:老師.下課鈴響了(),學生.下課鈴響了(),老師執行的是下班操作,學生執行的是放學操作,雖然二者訊息一樣,但是執行的效果不同


from abc import ABCMeta,abstractmethod
class Base(metaclass=ABCMeta):
    @abstractmethod
    def talk(self):
        pass

class Pig(Base):
    def talk(self):
        print("pig talk")


class People(Base):
    def talk(self):
        print("People talk")

def talk(obj):
    obj.talk()

pig = Pig()
people = People()
pig.talk()
people.talk()

輸出如下:

E:\python\python_sdk\python.exe E:/python/py_pro/python.py
pig talk
People talk

__屬性封裝

私有靜態屬性、私有方法

print("------封裝-------")
#封裝
#把屬性、方法藏在類裡面,在類內部呼叫
class Dog:
    # 私有靜態屬性
    __kind = "wangcai"

    #呼叫私有靜態屬性
    def getKind(self):
        return Dog.__kind
    #私有方法
    def __func(self):
        print("__func")
    #呼叫私有方法
    def func(self):
        self.__func()

#如下呼叫不提倡
print(Dog.__dict__)
print(Dog._Dog__kind)

#如下呼叫錯誤,因為需要在類內呼叫
# print(Dog.__kind)

d = Dog()
print(d.getKind())

#如下呼叫不提倡
d._Dog__func()
d.func()

輸出如下:

E:\python\python_sdk\python.exe E:/python/py_pro/python.py
------封裝-------
{'__module__': '__main__', '_Dog__kind': 'wangcai', 'getKind': <function Dog.getKind at 0x02BB1F60>, '_Dog__func': <function Dog.__func at 0x02BB1F18>, 'func': <function Dog.func at 0x02BB1ED0>, '__dict__': <attribute '__dict__' of 'Dog' objects>, '__weakref__': <attribute '__weakref__' of 'Dog' objects>, '__doc__': None}
wangcai
wangcai
__func
__func

私有物件屬性

#私有物件屬性

class Room:
    def __init__(self,name,area):
        self.name = name
        self.__area = area

    def getArea(self):
        return self.__area

room = Room("safly",100)
print(room.name)
print(room.getArea())
#不能如下方法呼叫私有物件屬性
# print(room.__area)

輸出如下:

E:\python\python_sdk\python.exe E:/python/py_pro/python.py
safly
100

私有屬性不能被繼承

#私有方法不能被繼承
class A:
    __role = "role"
    def __func(self):
        print("__a func")

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

class B(A):
    #如下的方法是錯誤的
    def getRole(self):
        return B.__role

a = A("safly")
print(dir(a))
print("-------------")
b = B("safly")
print(dir(b))

#呼叫報錯AttributeError: type object 'B' has no attribute '_B__role'
# print(b.getRole())

輸出如下:

E:\python\python_sdk\python.exe E:/python/py_pro/python.py
['_A__func', '_A__name', '_A__role', '__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__']
-------------
['_A__func', '_A__name', '_A__role', '__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__', 'getRole']

property 方法轉屬性

@property 把一個方法 偽裝成一個屬性
1.屬性的值 是這個方法的返回值
2.這個方法不能有引數了
3.類不能呼叫,只能物件呼叫

class Person:
    def __init__(self,name,height,weight):
        self.name = name
        self.height = height
        self.weight = weight
    @property
    def bmi(self):
        return self.weight / (self.height**2)

    @property
    def methdd(self):
        print("method")

per = Person("safly",1.73,75)
print(per.bmi)
per.methdd
#如下呼叫沒有效果
Person.methdd

輸出如下:

E:\python\python_sdk\python.exe E:/python/py_pro/python.py
25.05930702662969
method

property-setter設定值

class Goods:
    discount = 0.8
    def __init__(self,name,price):
        self.name = name
        self.price = price

    @property
    def getPrice(self):
        return self.price * Goods.discount

    @getPrice.setter
    def getPrice(self,newPrice):
        self.price= newPrice


app = Goods("apple",10)
print(app.getPrice)

app.getPrice = 20

print(app.getPrice)

輸出如下:

E:\python\python_sdk\python.exe E:/python/py_pro/python.py
8.0
16.0

deleter



class Foo:
    name = "Foo"
    @property
    def AAA(self):
        print('get的時候執行我啊')

    @AAA.setter
    def AAA(self,value):
        print('set的時候執行我啊',value)

    @AAA.deleter
    def AAA(self):
        del Foo.name
        print('delete的時候執行我啊')

#只有在屬性AAA定義property後才能定義AAA.setter,AAA.deleter
f1=Foo()

f1.AAA
#setter
f1.AAA='aaa'
print(Foo.name)
#deleter
del f1.AAA
#刪除完畢後,再次呼叫報如下錯誤
#AttributeError: type object 'Foo' has no attribute 'name'
# print(Foo.name)

輸出如下:

E:\python\python_sdk\python.exe E:/python/py_pro/python.py
get的時候執行我啊
set的時候執行我啊 aaa
Foo
delete的時候執行我啊

在del f1.AAA後,刪除完畢後,再次呼叫print(Foo.name)報如下錯誤
AttributeError: type object ‘Foo’ has no attribute ‘name’

內建property函式

一個靜態屬性property本質就是實現了get,set,delete三種方法

class Foo:
    def get(self):
        print('get的時候執行我啊')

    def set(self,value):
        print('set的時候執行我啊',value)

    def delet(self):
        print('delete的時候執行我啊')
        #def __init__(self, fget=None, fset=None, fdel=None, doc=None)
    AAA=property(get,set,delet) #內建property三個引數與get,set,delete一一對應

f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA

輸出如下:

E:\python\python_sdk\python.exe E:/python/py_pro/python.py
get的時候執行我啊
set的時候執行我啊 aaa
delete的時候執行我啊

classmethod

下面程式碼中我分別列出了,在不使用classmethod類方法,和使用類方法的2種寫法
@classmethod也不需要self引數,但第一個引數需要是表示自身類的cls引數
不管這個方式是從例項呼叫還是從類呼叫,它都用第一個引數把類傳遞過來


#classmethod
#需要使用靜態變數,且不需要跟物件相關
class Goods:
    __discount = 0.8

    @classmethod
    def change(cls,newPrice):
        cls.__discount = newPrice
    @classmethod
    def getPrice(cls):
        return cls.__discount

#之前的使用方式
    def change1(self,newPri):
        Goods.__discount = newPri
    def getPrice1(self):
        return Goods.__discount

Goods.change1(Goods,30)
print(Goods.getPrice1(Goods))

Goods.change(20)
print(Goods.getPrice())

輸出如下:

E:\python\python_sdk\python.exe E:/python/py_pro/python.py
30
20

staticmethod

staticmethod不需要表示自身物件的self和自身類的cls引數,就跟使用函式一樣。

class A:
    def func(self,name):
        print(name)

    def func1(name):
        print(name)

    @staticmethod
    def func2(self,name):
        print(name)

    @staticmethod
    def func3(name):
        print(name)


A.func(A,"safly")
A.func1("safly")

A.func2(A,"safly")
A.func3("safly")

輸出如下:

safly
safly
safly
safly

總結類、物件可以呼叫的方法



# staticmethod self\cls失效,類物件、例項物件均可以呼叫
# classmethod cls不管類物件、例項物件,輸出均是類物件
class A:
    @staticmethod
    def func1(name):
        print("func1",name)

    @staticmethod
    def func2(self,name):
        print("func2",self,name)

    @classmethod
    def func3(cls):
        print("func3",cls)

    @classmethod
    def func4(cls,name):
        print("func4",cls,name)

    @classmethod
    def func5(self,name):
        print("func5",self,name)

    @classmethod
    def func6(cls,self,name):
        print("func6",cls,self, name)

    def func7(self,name):
        print("func7",name)

    def func8(name):
        print("func8",name)

print("-----類呼叫--------")
A.func1("safly")
A.func2("A","safly")
A.func3()
A.func4("safly")
A.func5("safly")
A.func6("A","A")
A.func7(A,"safly")
A.func8("safly")

# print("----物件呼叫------")
a = A()
a.func1("safly")
a.func2("A","safly")
a.func3()
a.func4("safly")
a.func5("safly")
a.func6("A","A")
a.func7("safly")
a.func8()

輸出如下:

E:\python\py_dev\venv\Scripts\python.exe "E:/python/py_dev/python/03 python模擬登陸MysQL版.py"
-----類呼叫--------
func1 safly
func2 A safly
func3 <class '__main__.A'>
func4 <class '__main__.A'> safly
func5 <class '__main__.A'> safly
func6 <class '__main__.A'> A A
func7 safly
func8 safly
func1 safly
func2 A safly
func3 <class '__main__.A'>
func4 <class '__main__.A'> safly
func5 <class '__main__.A'> safly
func6 <class '__main__.A'> A A
func7 safly
func8 <__main__.A object at 0x059B4750>

Process finished with exit code 0

我們看到a.func5(“safly”)這樣的呼叫方式是錯誤的,其他的方式是可以的,這就為我們增加了呼叫方法的寫法