(九)属性自省

(一)私有属性

定义:类里面定义的变量叫类属性,那么类属性有两种,分别公有属性和私有属性。

私有属性定义:

  • 单下划线开头:_attr                    在外部可以直接访问
    • 双下划线开头:__attr           在外部不能直接访问,被改名了,所以在外部访问不了,改成了_Test_attr2
class Test(object):
    _attr = 100  # 在外部可以直接访问
    __attr2 = 200  # 在外部不能直接访问,被改名了,所以在外部访问不了,改成了_Test_attr2


t = Test()

# 查看该类的所有的属性和方法
print(Test.__dict__)



# 打印结果
{'__module__': '__main__', '_attr': 100, '_Test__attr2': 200, '__dict__': <attribute '__dict__' of 'Test' objects>, '__weakref__': <attribute '__weakref__' of 'Test' objects>, '__doc__': None}

注意: python并没有真正的私有化支持,但是可以用下划线得到伪私有,有一项大多数python 代码都遵循的习惯:带有下划线,前缀的名称应被视为非公开的API的一部分(无论是函数、方法还是数据成员)。它应被视为实现细节,如有更改,恕不另行通知。

(二)__dict__

  • 类调用__dict__属性,返回一个字典(类属性和方法)
  • 实例调用__dict__属性,返回的是实例相关的实例属性
class MyTest:
    name = 100
    age = 99

    def __init__(self, a, b):
        self.a = a
        self.b = b


m = MyTest(1, 2)
print(m.__dict__)  # 传的如果是对象, 那么就是获取对象的实例属性
print(MyTest.__dict__)  # 获取类的属性和方法

ddd = dir(m)  # 1、获取的只是属性的名字,没有值。 2、把父类里面的能够使用的属性都获取出来了。
print(ddd)

 有童鞋提到了dir这个内置函数是干啥用的,他跟__dict__的区别在于:

  • __dict获取的是属性和对应的值以键值对的形式生成,打印出来是一个字典,dir这个内置函数获取的只是属性的名字。
  • dir内置函数把对象只要是能够使用的方法(包括父类)里的方法全部打印出来了。

(三)内置属性__slots__ 

作用1:限制对象可以设置的属性

默认情况下,类的实例有一个字典用于存储属性。这对于具有很少实例变量的对象会浪费空间。当创建大量实例时,空间消耗可能会变得尖锐。

可以通过在类定义中定义__slots__来覆盖默认__dict__行为。__slots__声明接受一个实例变量序列,并在每个实例中只保留足够保存每个变量值的空间。因为没有为每个实例创建__dict__,所以节省空间。

下面我们创建一个list类型的对象,我们看看能不能设置li的name属性=999,通过打印结果发现不能设置,为什么呢?为什么我创建了一个类我能随意设置这个类的对象的属性呢?

li = list()
li.name = 999
print(li)



# 打印结果
AttributeError: 'list' object has no attribute 'name'
class MyTest:
    pass


m = MyTest()
m.name = 'wangBigxia'
print(m.__dict__)




# 打印结果
{'name': 'wangBigxia'}

 那这个时候我们在MyTest这个类里面加个__slots__ = [] 试试看,发现我们设置m.name会报错,m.__dict__他也会报错,为什么呢?这就是__slot_的作用:限制对象可以设置的属性。

因为设置的__slots__ = [] ,设置的为空列表,那这个对象任何属性都加不了。

那我们修改代码:__slots__ = ['name']试试看:

name属性可以设置,但是age属性设置不了。因为我__slots__ = ['name']

 类里面__init__给类的实例化对象初始化属性的时候,如果__slots__设置了他属性范围,那么init方法中就不允许设置初始化其他属性。

 实用小技巧:当你这个类会实例化非常非常多的时候,可以通过__slots__阻止这个__dict__的创建。

众所周知,每实例化一个对象,就会自动生成__dict__ 方法。当你这个类会实例化非常非常多的时候,通过这个方法可以节约内存开销。

(四)自定义属性访问

可以定义下面的方法来自定义类实例的属性访问的含义(访问、赋值或者删除x.name)

  • object.__getattr__                     当属性查找在通常的地方没有找到该属性时调用
  • object.__getattribute__             查找属性时,第一时间会调用该方法
  • object.__setattr__                     设置属性时,调用该方法设置属性
  • ojbect.__delattr                         在 del obj.attr删除属性时触发 

1、获取属性

我创建了一个Demo类,我这个时候实例化一个对象,然后调用他name属性的时候,报错了,因为他没有name属性对吧。


这个时候我们重新创建一个类,然后再调用一下试试看

由打印结果可以看出来,他打印的是none,为什么没有报错呢?因为当你使用对象.name查找属性的时候,其实第一时间调用的其实是__getattribute__方法。


因为我们没有实现找属性的代码,所以他打印出来是None,但是我们怎么该让他去实现自己去找属性的代码呢?我们其实可以调用父类的__getattribute__方法。

当我们执行t.name的时候,先是调用__getattribute__方法,因为这个方法里调用了父类的查找元素的方法,然后t对象没有name属性,就会抛出异常,没有找到属性时,触发AttributeError异常时会调用__getattr__方法

 那我们在__getattribute__方法中除了调用父类的__getattribute__方法以外,还要干什么,我们需要将找到的结果保存起来 并且返回,上代码。

2、设置属性

3、删除属性

 同设置属性查询属性一样的道理,就不赘述了。

(五)自定义属性管理机制的案例

Demo1 :定义一个MyClass类:可以初始化name,age两个属性
                         name属性的值只能设置为str
                         age属性的值只能设置为int
                         name属性不能被删除
                         age输出查找出来的结果如果少于0,返回0

"""
定义一个MyClass类:可以初始化name,age两个属性
    name属性的值只能设置为str
    age属性的值只能设置为int
    name属性不能被删除
    age输出查找出来的结果如果少于0,返回0


"""
class MyClass:

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

    def __setattr__(self, key, value):
        if key == 'name':
            if isinstance(value, str):
                super().__setattr__(key, value)
            else:
                raise TypeError('name属性只能设置为字符串')
        elif key == 'age':
            if isinstance(value, int):
                super().__setattr__(key, value)
            else:
                raise TypeError('age属性只能设置为int')
        else:
            super().__setattr__(key, value)


m = MyClass('musen', 18)
print(m.name, m.age)

(六)序列类型数据索引取值对于的魔术方法

我们先自己手写一个类,我们为啥不能通过对象[属性名]获取他的属性值呢,是因为我们手写的这个类没有实现__getitem__这个方法。

 我们修改一下代码试试看:

 

下面上传源代码,大家可以自己尝试一下: 

class MyClass(object):
    def __init__(self, a, b, c, d):
        self.a = a
        self.b = b
        self.c = c
        self.d = d

    def __getitem__(self, item):
        print('Item:', item)
        return getattr(self, item)

    def __setitem__(self, key, value):
        setattr(self, key, value)

    def __delitem__(self, key):
        print("删除索引:", key)
        delattr(self, key)

    def __len__(self):
        """len(obj)"""
        return len(self.__dict__)
        # return 999


m = MyClass(11, 22, 33, 44)

# print(m['d'])
#
# m['abc'] = 11111
#
# print(m['abc'])
#
# del m['b']

# res = len(m)
# print(res)

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值