天天看點

python學習之 getattr vs __get__ vs __getattr __ vs __getattribute__ vs __getitem__

 1. getattr、setattr、hasattr

  getattr比較常用,與setattr和hasattr一起出現,他們也是最容易了解的,下面是他的用法:

class Profile():
     name="xiaoxin"
     def sex(self):
          return "male"p=Profile()
hasattr(p, "name")    # 判斷屬性是否存在
>>> True
hasattr(p, "age")    # 判斷屬性是否存在
>>> False
getattr(p, "name")  # 擷取屬性值
>>> xiaoxin
getattr(p, "sex")
>>> <bound method Profile.sex of <__main__.Profile object at 0x7f22608f9710>>
getattr(p, "age", 14)  # 如果屬性不存在,則傳回預設值
>>> 14
setattr(p, "age", "26")    # 為屬性指派,并沒有傳回值
hasattr(p, "age")    # 屬性存在了
>>> True      

2. __get__ 、 __set__、 __delete__

  提起__get__, 就不能不說  __set__, __delete__ , 一個類,隻要其内部定義了方法 __get__, __set__, __delete__ 中的一個或多個,就可以稱其為描述符。如果同時定義了

__get__()

__set__()

,其被認為是一個資料描述符。隻定義

__get__()

的描述符被稱為非資料描述符。

  描述符常被用來作類型檢查, 下面看它的具體用法:

class Integer:
    def __init__(self, name):
        self.name = name

    def __get__(self, instance, owner):
     if instance is None:
            return self
        else:
            return instance.__dict__[self.name]

    def __set__(self, instance, value):
     if not isinstance(value, int):
            raise TypeError('excepted an int')
        instance.__dict__[self.name] = value

    def __delete__(self, instance):
      del instance.__dict__[self.name]


class Point:
    x = Integer('x')
    y = Integer('y')

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

p = Point(2, 3)
p.x 
>> 2
p.y 
>> 3
p = Point(2.5, 3)
>> TypeError: excepted an int      

  需要注意的是,對象屬性的通路順序:執行個體屬性>類屬性>父類屬性>__getattr__(),當Python解釋器發現執行個體對象的字典中,有與描述符同名的屬性時,描述符優先,會覆寫掉執行個體屬性。

 3. __getattr__ 、__setattr__ 、__delattr__

  從對象中讀取某個屬性時,首先需要從self.__dicts__中搜尋該屬性,如果__dict__中沒有該屬性, 再從__getattr__中拿到傳回值。當設定屬性時,則會觸發__setattr__方法,在這裡可以進行類型驗證(描述符隻會對定義的描述符屬性進行類型驗證, 而__setattr__會對所有屬性進行類型驗證)

class Point:

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

    def __getattr__(self, item):
        print(f'getattr {item}')
        return None

    def __setattr__(self, key, value):
        print(f'setattr {key}:{value}')
        if isinstance(value, int):
            return object.__setattr__(self, key, value)

    def __delattr__(self, item):
        print(f"delattr {item}")
        del self.__dict__[item]

      
p = Point(2, 3)      

>>> setattr x:2

>>> setattr y:3

p.x

>>> 3

p.c

>>> getattr c

>>> None

p.c = 9

>>> setattr c:9

p.c

>>> 9

4. __getattribute__ 

   無論調用屬性還是方法,都是先強制調用 __getattribute__ 方法,然後再傳回屬性的值或者是 函數(方法)的引用。

class Point:

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

    def __getattribute__(self, item):   # 在 __getattribute__ 方法裡面不要在出現self.**這種調用,因為每次調用類的屬性都會強制調用 __getattribute__ ,會造成遞歸調用
        return 'i get you ' if item == 'x' else object.__getattribute__(self, item)

    def __getattr__(self, item):
        print(f'getattr {item}')
        return None
      
p = Point(2, 3)
p.x
>>> i get you
p.y
>>> 3
p.c   # 首先調用 __getattribute__ , 裡面實作了 查找__dict__裡面有沒有這個鍵,如果沒有再調用 __getattr__方法
>>> getattr c
>>> None      

 5. __getitem__、__setitem__、__delitem__

這三個方法主要用于對集合的操作

可變集合需要實作: __len__  __getitem__    __setitem__  __delitem__

不可變集合需要實作: __len__  __getitem__

__len__:傳回集合長度

__getitem__(self, item) 使用索引通路元素

__setitem__(self, key, value) 對索引指派,使用 self[key] = value 。

__delitem__(self, key) 删除索引值 del self[key]

__contains__ 實作in運算符,如果沒有實作這個方法python也會調用__getitem__來使in運算符可用

class TemTest:

    def __init__(self,):
        self.x = [i for i in range(10)]

    def __len__(self):
        return len(self.x)

    def __getitem__(self, item):return self.x[item]

    def __setitem__(self, key, value):
        self.x[key] = value

    def __delitem__(self, key):
        del self.x[key]

    def __contains__(self, item):
        return item in self.x

    def __repr__(self):
        return '{}'.format(self.x)      
test=TemTest() #執行個體化
print(len(test)) #傳回長度
print(test[0])   #列印下标0的值
print(test[:3])  #切片
test[3]=10       #将下标3的值替換為10
print(test)
del test[3]      #删除下标3的值
print(test)
print(1 in test)  #測試in運算符
print(3 in test)        
10
0
[0, 1, 2]
[0, 1, 2, 10, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 4, 5, 6, 7, 8, 9]
True
False      

參考博文:https://www.cnblogs.com/flashBoxer/p/9645939.html