常用的内置属性
在类中包含着很多默认的内置属性,当我们创建一个类时,如果有必要,我们可以进行重载。Python为我们提供了标准数据类型,以及丰富的内置函数,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增或者改写方法,这就用到了继承/派生知识。
一、attr系列
_ _getattr_ _
属性不存在时会自动触发
_ _setattr_ _
设置属性的时候会触发
_ _delattr_ _
设置属性的时候会触发
_ _getattribute_ _
程序执行时调用(无论属性存在与否),当它使用raise关键字抛出AttributeError异常时,由_ _getattr_ _内置属性捕捉进而进行处理。
class Test: def __init__(self, book_name): self.book_name = book_name #属性不存在时会自动触发 def __getattr__(self, item): print("访问的属性不存在时执行的操作") #初始化时会执行 def __setattr__(self, key, value): if type(value) is str: #value先转换一下在设置,这样比较好玩 self.__dict__[key] = value.upper() print("已经设置") else: print("参数必须是str") #删除属性时会自动触发 def __delattr__(self, item): print("执行删除属性【%s】的操作"%item) #测试 test = Test("从你的世界路过") #输出:已经设置 print(test.__dict__) #输出:{'book_name': '从你的世界路过'} del test.book_name #输出:执行删除属性【book_name】的操作 print(test.__dict__) #输出:{'book_name': '从你的世界路过'}
二、_ _del_ _
当对象在内存中被释放时,自动触发执行。如果产生的对象仅仅只是python程序级的(用户级),那么无需定义_ _del_ _,如果产生的对象的同时还会向操作系统发起系统调用,即一个对象有用户级与内核级两种资源,比如(打开一个文件,创建一个数据库链接),则必须在清除对象的同时回收系统资源,这就用到了_ _del_ _。需要注意的是,我们删除实例的属性时是不会的执行_ _del_ _的,只有在删除实例时才会触发_ _del_ _。系统在回收内存时也会触发它。比如当我们创建数据库类时,用该类实例化出数据库链接对象,对象本身是存放于用户空间内存中,而链接则是由操作系统管理的,存放于内核空间内存中。当程序结束时,python只会回收自己的内存空间,即用户态内存,而操作系统的资源则没有被回收,这就需要我们重载del,在对象被删除前向操作系统发起关闭数据库链接的系统调用,回收资源。
class Person: def __init__(self, name): self.name = name def __del__(self): print("执行删除实例的操作") #测试 person = Person("SB") del person.name #不执行 del person #执行删除实例的操作
三、_ _slots_ _
在java中想要定义一个类变量可以用static关键字修饰,再Python中当然也提供了类变量。Python以内置属性的方式向用户提供类变量,在任意一个类中通过重载_ _slots_ _内置属性可以获得类变量。Python的类变量可以是序列或可迭代对象。
Python为每一个实例提供了一个独自的字典。我们常用的“.”运算符在底层其实操作的是字典。当我们一个类有很多的实例时,每一个实例都会有一个单独的内存空间,有时候这种做法是不必要的,不可取的。所以这就是类变量的诞生初衷。Python的类变量对所有的实例对象取消了字典,实例使用一种更加紧凑的内部表示,通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个字典,这跟元组或列表很类似。类变量列出的属性名在内部被映射到这个数组的小标上。
Python类变量的很多特性都依赖于普通的基于字典的实现。它限制了实例对象的访问操作,当访问的属性不存在时,会直接报错。而使用类变量的弊端是不再支持基于字典的特性,比如多继承等等。
class Person: __slots__ = ["name","age"] #测试 person = Person() print(Person.__slots__) #输出:['name', 'age'] person.name = "小明" person.age = 18 print(person.name) #输出:小明 print(person.age) #输出:18 #输出:AttributeError: 'Person' object has no attribute'__dict__' print(person.__dict__)
四、_ _format_ _
_ _format_ _内置属性是一种自定义的格式化。通常 Python自带的format格式化通常无法满足我们的需求,因此我们需要对其进行修改。
#定义格式字典 format_dic={ 'ymd':'{0.year}{0.mon}{0.day}', 'm-d-y':'{0.mon}-{0.day}-{0.year}', 'y:m:d':'{0.year}:{0.mon}:{0.day}' } class Date_format: def __init__(self,year,mon,day): self.year=year self.mon=mon self.day=day def __format__(self, format_spec): if not format_spec or format_spec not in format_dic: format_spec='ymd' fm=format_dic[format_spec] return fm.format(self) #测试 date = Date_format(2016,12,26) print(format(date,'ymd')) #输出:20161226 print(format(date,'y:m:d')) #输出:2016:12:26 print(format(date,'m-d-y')) #输出:12-26-2016 print(format(date,'m-d:y')) #输出:20161226
五、_ _call_ _
我们知道构造函数的执行是由创建实例对象触发的,即:对象 = 类名()。Python中一切皆对象,我们创建的类本身也是一个对象。内置属性_ _call_ _的执行是由对象的创建而触发的。重载_ _call_ _的意义在于将对象作为函数使用,_ _call_ _不影响对象的生命周期,不影响一个对象的构造和析构。
class Test: def __init__(self, name): self.name = name print("执行__init__") def __call__(self, *args, **kwargs): print("执行__call__") #测试 test = Test("SB") #输出:执行__init__ test() #输出:执行__call__
六、_ _next_ _ 和 _ _iter_ _
在迭代器中我们说实现了迭代器协议的对象称为可迭代对象。而迭代器协议强调对象必须提供一个next或_ _next_ _()方法,并且执行该方法只有两种决策,要么返回迭代中的下一项,要么者引起一个StopIteration异常,以终止迭代。其实这个协议也不过是通过内置属性实现的。我们知道一个普通的对象是无法对它进行遍历的,但是Python提供了_ _next_ _和_ _iter_ _这两个内置属性供我们重载对象。通过这两个内置属性的使用使得普通对象可变为可迭代对象。而迭代器的实现也基于此。
class Test: def __init__(self, start, stop): self.start = start self.stop = stop def __iter__(self): return self def __next__(self): if self.start >= self.stop: raise StopIteration("索引越界") num = self.start self.start += 1 return num #测试 test = Test(1,10) for i in test: #遍历对象 print(i, end=' ')
#斐波那契数 class Fib: def __init__(self): self._a=0 self._b=1 def __iter__(self): return self def __next__(self): self._a,self._b=self._b,self._a + self._b return self._a #测试 f1=Fib() for i in f1: if i > 100: break print('%s ' %i,end='')
七、_ _str_ _ 和 _ _repr_ _
当我们使用str函数并以对象作为参数的时候,返回的是一个对象的地址及所属类。它的默认执行路径是:str( obj ) ==> obj._ _str_ _()。str 与 repr 都用于控制输出,当他们在程序中共存时,repr不会执行;当程序中只有repr时,虽然它被用于解释器,但是在控制台会执行,因为str找不到,所以它是str的替代品。
class Person: #系统默认输出对象的地址及所属类 def __str__(self): return "修改系统默认的内置属性" #测试 person = Person() print(person) #输出:修改系统默认的内置属性
class Student: def __init__(self, name, age): self.name = name self.age = age def __str__(self): return "名字是%s,年龄是%s"%(self.name, self.age) def __repr__(self): return "执行__repr__" stu = Student("小明",18) print(stu) #输出:名字是小明,年龄是18