類的兩個裝飾器classmethod、staticethod和內置魔術方法

一、兩個裝飾器@classmethod、@staticmethod

@classmethod:把類中的綁定方法變成一個類方法,cls 就等於類名

有什麼用?

1、在方法中任然可以引用類中的靜態變數

2、可以不用實例化對象,就直接用類名在外部調用這個方法

什麼時候用?

1、定義了一個方法,默認傳 self ,但這個 self 沒有被使用。

2、並且你在這個方法里用到了當前類名,或者你準備使用這個類的記憶體空間中的名字的時候

# 商品打折:
class Goods:
    __discount = 0.8def __init__(self, original_price):
        self.original_price = original_price
        self.price = self.original_price * self.__discount
​
    @classmethod        # 把一個對象的綁定方法改成一個類方法
    def change_discount(cls, count):
        cls.__discount = count  # 相當於Goods.__discount = count
​
​
Goods.change_discount(0.6)      # 類方法可以通過類名調用
# 實例化一個華為手機對象
huawei = Goods(20)
print(int(huawei.price))
​
huawei.change_discount(0.4)     # 類方法可以通過對象調用
# 實例化一個蘋果手機對象
apple = Goods(20)
print(int(apple.price))
​
# 輸出
12
8

擴展:

在掉用類的函數的時候就實例化了一個對象

import time
​
class Date:
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day
​
    @classmethod
    def today(cls):
        structure = time.localtime()
        # 在方法中創建一個對象
        obj = cls(structure.tm_year, structure.tm_mon, structure.tm_mday)
        return obj
​
​
# 當調用類中的today方法就會返回一個對象(對象屬性不用自己填,today方法已經填好了)
data對象 = Date.today()
print(data對象.year)
print(data對象.month)
print(data對象.day)
​
# 輸出
2021
5
4

@staticmethod:被裝飾的方法會成為一個靜態方法

本身是一個普通函數,被挪到類的內部執行,那麼直接給這個函數添加 @staticmethod 裝飾器就可以了

在函數的內部即用不到 self 變數,也用不到 cls 類

class User:
    pass
​
    @staticmethod
    def login(user):
        print(f'{user},登錄成功')
​
User.login('Alen')   # 類可以調佣
Bob = User()
Bob.login('Bob')     # 對象可以調佣
# 輸出
Alen,登錄成功
Bob,登錄成功

總結:能定義到類中的內容。

1、靜態變數:是個所有的對象共享的變數 ——由對象、類調用

2、實例變數:是對象的屬性變數 ——由對象調用

3、綁定方法:是個自帶self參數的函數 ——由對象調用

4、類方法:是個自帶cls參數的函數 ——由對象、類調用

5、property屬性:是個偽裝屬性的方法 ——由對象調用,但不加括弧

二、內置魔術方法

__call__方法:對象 + ( ) 調用這個類中的__call__方法

class User:
    pass
​
    @staticmethod
    def login(user):
        print(f'{user},登錄成功')
​
User.login('Alen')   # 類可以調佣
Bob = User()
Bob.login('Bob')     # 對象可以調佣
# 輸出
Alen,登錄成功
Bob,登錄成功

__len__方法:len (對象) 需要實現這個類中的__len__方法

class A:
    def __init__(self):
        self.lis = [1, 2, 3, 4, 5]
​
    def len(self):
        return len(self.lis)
​
    def __len__(self):
        return len(self.lis)
​
​
a = A()
print(len(a))       # 可以直接len(a)就可以查看lis裡面值的個數
print(a.len())      # 不然就要使用類中的len方法才可以查看
# 輸出
5
5# -------------------------------------------------------------------------------------
# 擴展:也可以使用自定義的函數
class A:
    def __init__(self, count):
        self.count = count
​
    def __square__(self):
        value = self.count ** 2
        return value
​
def square(count):
    return A.__square__(count)
​
a = A(5)
​
print(square(a))
​
# 輸出
25

__new__方法:實例化的時候會先執行__new__方法用來創建一個對象需要的空間

class A:
    def __new__(cls, *args, **kwargs):
        o = super().__new__(cls)
        print(o)                    # 記憶體地址和 self 的記憶體地址一樣
        print('執行了__new__方法')
        return o
​
    def __init__(self):
        print(self)
        print('執行了__init__方法')
​
A()     # 實例化對象
# 輸出
<__main__.A object at 0x014E0970>
執行了__new__方法
<__main__.A object at 0x014E0970>
執行了__init__方法

__new__方法的設計模式:————>單例模式

一個類,從頭到尾,只會創建一次 self 的空間

例如:我有一輛車(有且只有一輛),小紅要拿去用,我就給她了,她拿去隨便怎麼改裝,換個紅色車漆,換藍色車輪都可以。但是小明又過來找我要用車,那我就去把我的車給拿回來在給小明,也隨小明隨便改裝。

class Car:
    __attribute = None
​
    def __new__(cls, *args, **kwargs):
        if cls.__attribute is None:         # 當第一次開空間的時候__attribute肯定是空的
            # 所以就會開一個新的空間。並把__attribute給賦值,那麼以後的都不是第一次了。
            cls.__attribute = super().__new__(cls)  
        return cls.__attribute                      # 把新空間返回給self
def __init__(self, car_paint, cartwheel_colour):
        self.car_paint = car_paint
        self.cartwheel_colour = cartwheel_colour
​
​
xiao_hong = Car('紅色的車漆', '藍色的車輪')
print('小紅的記憶體地址:', xiao_hong)    # 他們的記憶體地址都是一樣的
print(xiao_hong.car_paint)          # 紅色的車漆————>小紅剛拿到的時候還是紅色的
xiao_ming = Car('白色的車漆', '黑色的車輪')
print('小名的記憶體地址:', xiao_ming)
print(xiao_hong.car_paint)          # 白色的車漆————>小明在拿過來,把顏色給改成白的了
print(xiao_ming.car_paint)          # 白色的車漆————>小紅的也變了,應為他們都是同一個車
# 輸出
小紅的記憶體地址: <__main__.Car object at 0x01A70A60>
紅色的車漆
小名的記憶體地址: <__main__.Car object at 0x01A70A60>
白色的車漆
白色的車漆

__str____repr__方法:

str方法:幫助我們在列印或展示對象的時候更加直觀的顯示對象內容

1、在列印一個對象的時候,調用__str__方法

2、在 %s 拼接一個對象的時候,調用__str__方法

3、在str一個對象的時候,調用__str__方法

當列印一個對象的時候,希望這個對象顯示的值是什麼,那麼你就必須在這個對象內部實現一個__str__ ,這樣的話你就能做到,你在列印這個對象的時候你就能查看到他的內容是多少(查看的內容由你自己在 str 中定義),不然就只能答應一堆記憶體地址。

repr方法:是 str 的備胎(有str的時候列印str,沒有就列印rper),同時 %r 和 repr 有合作關係

class User:
    def __init__(self, name, age):
        self.name = name
        self.age = age
​
    def __str__(self):
        return self.name
​
    def __repr__(self):
        return self.age
​
​
Bob = User('鮑勃', '18')
​
print(Bob)  # 現在就可以直接列印對象了,——不設置str方法,列印的就是是一堆記憶體地址,優先列印 str 方法
print('我的名字是%s' % Bob)  # 在 %s 拼接一個對象的時候,調用__str__方法
print('我的年齡是%r' % Bob)  # 在 %r 拼接一個對象的時候,調用__repr__方法
# 輸出
鮑勃
我的名字是鮑勃
我的年齡是18