四个可以实现自省(反射)的函数
下列方法适用于类和对象(一切皆对象,类本身也是一个对象)
instance
通过instance.name
访问属性name
,只有当属性name
没有在实例的__dict__
或它构造类的__dict__
或基类的__dict__
中没有找到,才会调用__getattr__
。name
可以通过正常机制追溯到时,__getattr__
是不会被调用的。如果在__getattr__(self, attr)
存在通过self.attr
访问属性,会出现无限递归错误。class ClassA(object):
def __init__(self, classname):
self.classname = classname
def __getattr__(self, attr):
return('invoke __getattr__', attr)
insA = ClassA('ClassA')
print(insA.__dict__) # 实例insA已经有classname属性了
# {'classname': 'ClassA'}
print(insA.classname) # 不会调用__getattr__
# ClassA
print(insA.grade) # grade属性没有找到,调用__getattr__
# ('invoke __getattr__', 'grade')
__setattr__
方法,当通过实例获取属性尝试赋值时,就会调用__setattr__
。常规的对实例属性赋值,被赋值的属性和值会存入实例属性字典__dict__
中。class ClassA(object):
def __init__(self, classname):
self.classname = classname
insA = ClassA('ClassA')
print(insA.__dict__)
# {'classname': 'ClassA'}
insA.tag = 'insA'
print(insA.__dict__)
# {'tag': 'insA', 'classname': 'ClassA'}
如下类自定义了__setattr__
,对实例属性的赋值就会调用它。类定义中的self.attr
也同样,所以在__setattr__
下还有self.attr
的赋值操作就会出现无线递归的调用__setattr__
的情况。自己实现__setattr__
有很大风险,一般情况都还是继承object
类的__setattr__
方法。
class ClassA(object):
def __init__(self, classname):
self.classname = classname
def __setattr__(self, name, value):
# self.name = value # 如果还这样调用会出现无限递归的情况
print('invoke __setattr__')
insA = ClassA('ClassA') # __init__中的self.classname调用__setattr__。
# invoke __setattr__
print(insA.__dict__)
# {}
insA.tag = 'insA'
# invoke __setattr__
print(insA.__dict__)
# {}
与 del 实例名.name 等价
def delattr(x, y): # real signature unknown; restored from __doc__
"""
Deletes the named attribute from the given object.
delattr(x, 'y') is equivalent to ``del x.y''
"""
pass
class Foo:
x=1
def __init__(self,y):
self.y=y
def __getattr__(self, item):
print('----> from getattr:你找的属性不存在')
def __setattr__(self, key, value):
print('----> from setattr')
# self.key=value #这就无限递归了,你好好想想
# self.__dict__[key]=value #应该使用它
def __delattr__(self, item):
print('----> from delattr')
# del self.item #无限递归了
self.__dict__.pop(item)
#__setattr__添加/修改属性会触发它的执行
f1=Foo(10)
print(f1.__dict__) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值
f1.z=3
print(f1.__dict__)
#__delattr__删除属性的时候会触发
f1.__dict__['a']=3#我们可以直接修改属性字典,来完成添加/修改属性的操作
del f1.a
print(f1.__dict__)
#__getattr__只有在使用点调用属性且属性不存在的时候才会触发
f1.xxxxxx
instance
通过instance.name
访问属性name
,__getattribute__
方法一直会被调用,无论属性name
是否追溯到。如果类还定义了__getattr__
方法,除非通过__getattribute__
显式的调用它,或者__getattribute__
方法出现AttributeError
错误,否则__getattr__
方法不会被调用了。如果在__getattribute__(self, attr)
方法下存在通过self.attr
访问属性,会出现无限递归错误。ClassA
中定义了__getattribute__
方法,实例insA
获取属性时,都会调用__getattribute__
返回结果,即使是访问__dict__
属性。class ClassA(object):
def __init__(self, classname):
self.classname = classname
def __getattr__(self, attr):
return('invoke __getattr__', attr)
def __getattribute__(self, attr):
return('invoke __getattribute__', attr)
insA = ClassA('ClassA')
print(insA.__dict__)
# ('invoke __getattribute__', '__dict__')
print(insA.classname)
# ('invoke __getattribute__', 'classname')
print(insA.grade)
# ('invoke __getattribute__', 'grade')
class Test:
def __setitem__(self, key, value):
print('setitem')
self.__dict__[key] = value
def __getitem__(self, item):
print('getitem')
return self.__dict__[item]
def __delitem__(self, key):
print('del')
self.__dict__.pop(key)
f = Test()
f.a = 123 #使用 . 的方式不会调用 __setitem__() 方法
f['a'] = 123 #使用 [] 的方式才会调用 __setitem__() 方法
print(f.a) # 使用 . 的方式不会调用 __getitem__() 方法
print(f['a']) # 使用 [] 的方式才会调用 __getitem__() 方法
# del f.a # 使用 . 的方式不会调用 __delitem__() 方法
del f['a'] # 使用 [] 的方式才会调用 __delitem__() 方法
# 使用 . 的方式 与 attr 系列方法有关。 使用 [] 的方式与 item 系列方法有关
print方法会调用str()方法。str方法会调用此__str__()方法 返回的必须是字符串,否则会异常、
不修改__str__()方法:
class Test:
pass
f = Test()
print(f)
输出:
<__main__.Test object at 0x00000000011238D0>
重写__str__()方法:
class Test:
def __str__(self):
return "这是Test类"
f = Test()
print(f)
输出:
这是Test类
与 __str__()类似 但是 与其不同的是__repr__()在解释器中触发,__str__()在print()中触发。 返回的必须是字符串,否则会异常、
print()函数只有当__str__()未定义。才会去调用__repr__()
必须返回一个字符串。
class Date:
def __init__(self, year, mon, day):
self.y = year
self.m = mon
self.d = day
def __format__(self, format_spec):
if not format_spec or format_spec not in format_dict: # 如果没有指定format_spec 或者不存在的格式就默认给他ymd格式
format_spec='ymd'
fmt = format_dict[format_spec]
return fmt.format(self)
d = Date("2018","4","9")
print(format(d))
print(format(d,'y-m-d'))
print(format(d,'m-d-y'))
1 1.__slots__是什么:是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性) 2 2.引子:使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的) 3 3.为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__ 4 当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个 5 字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给 6 实例添加新的属性了,只能使用在__slots__中定义的那些属性名。 7 4.注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,你应该 8 只在那些经常被使用到 的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象 。 9 关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。 更多的是用来作为一个内存优化工具。
用于限制class的属性 省内存。
使Test类只能设置__slots__()提供的两个属性。name和age
使用__slots__
要注意,__slots__
定义的属性仅对当前类起作用,对继承的子类是不起作用的,除非在子类中也定义__slots__
,这样,子类允许定义的属性就是自身的__slots__
加上父类的__slots__
。
class Test:
__slots__ = ['name','age'] # 类似于定制属性字典{'name':None, 'age':None}
t = Test()
t.name = 'zjs'
print(t.name)
#t.xxxxxxxxxxxxx = 'test' # 会调用setattr然后通过t.__dict__['xxxxxxxxx'] = 'test' 由于__dict__不存在所以不能设置
# t.__dict__ # 没有这个属性。
__dir__
会被调用。返回值必须是序列。dir()将返回的序列转换成列表并排序。
该属性不能被继承。
class Test:
'我是描述信息'
pass
print(Test.__doc__)
表示当前操作的对象在那个模块
表示当前操作的对象的类是什么
from lib.aa import C
obj = C()
print obj.__module__ # 输出 lib.aa,即:输出模块
print obj.__class__ # 输出 lib.aa.C,即:输出类
一般不需要定义。析构方法,当对象在内存中被释放时,自动触发执行。
注:如果产生的对象仅仅只是python程序级别的(用户级),那么无需定义__del__,如果产生的对象的同时还会向操作系统发起系统调用,即一个对象有用户级与内核级两种资源,比如(打开一个文件,创建一个数据库链接),则必须在清除对象的同时回收系统资源,这就用到了__del__
f=open('a.txt') #做了两件事,在用户空间拿到一个f变量,在操作系统内核空间打开一个文件
del f #只回收用户空间的f,操作系统的文件还处于打开状态
#所以我们应该在del f之前保证f.close()执行,即便是没有del,程序执行完毕也会自动del清理资源,于是文件操作的正确用法应该是
f=open('a.txt')
读写...
f.close()
很多情况下大家都容易忽略f.close,这就用到了with上下文管理
对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
Python中有一个有趣的语法,只要定义类型的时候,实现__call__
函数,这个类型就成为可调用的。换句话说,我们可以把这个类的对象当作函数来使用,相当于重载了括号运算符。
class Student(object):
def __init__(self, name):
self.name = name
def __call__(self):
print('My name is %s.' % self.name)
s = Student('Michael')
s()
# My name is Michael.
#_*_coding:utf-8_*_
__author__ = 'Linhaifeng'
class Foo:
def __init__(self,x):
self.x=x
def __iter__(self):
return self
def __next__(self):
n=self.x
self.x+=1
return self.x
f=Foo(3)
for i in f:
print(i)
简单示范
class Foo:
def __init__(self,start,stop):
self.num=start
self.stop=stop
def __iter__(self):
return self
def __next__(self):
if self.num >= self.stop:
raise StopIteration
n=self.num
self.num+=1
return n
f=Foo(1,5)
from collections import Iterable,Iterator
print(isinstance(f,Iterator))
for i in Foo(1,5):
print(i)
class Range:
def __init__(self,n,stop,step):
self.n=n
self.stop=stop
self.step=step
def __next__(self):
if self.n >= self.stop:
raise StopIteration
x=self.n
self.n+=self.step
return x
def __iter__(self):
return self
for i in Range(1,7,3): #
print(i)
练习:简单模拟range,加上步长
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()
print(f1.__next__())
print(next(f1))
print(next(f1))
for i in f1:
if i > 100:
break
print('%s ' %i,end='')
斐波那契数列
使用继承的二次加工(包装):
class List(list): #继承list所有的属性,也可以派生出自己新的,比如append和mid
def append(self, p_object):
' 派生自己的append:加上类型检查'
if not isinstance(p_object,int):
raise TypeError('must be int')
super().append(p_object)
@property
def mid(self):
'新增自己的属性'
index=len(self)//2
return self[index]
l=List([1,2,3,4])
print(l)
l.append(5)
print(l)
# l.append('1111111') #报错,必须为int类型
print(l.mid)
#其余的方法都继承list的
l.insert(0,-123)
print(l)
l.clear()
print(l)
使用授权的二次加工(包装):
class List(list):
def __init__(self,item,tag=False):
super().__init__(item)
self.tag=tag
def append(self, p_object):
if not isinstance(p_object,str):
raise TypeError
super().append(p_object)
def clear(self):
if not self.tag:
raise PermissionError
super().clear()
l=List([1,2,3],False)
print(l)
print(l.tag)
l.append('saf')
print(l)
# l.clear() #异常
l.tag=True
l.clear()
2.通过使用__setattr__
, __getattr__
, __delattr__
可以重写dict,使之通过“.”调用键值。
class Dict(dict):
'''
通过使用__setattr__,__getattr__,__delattr__
可以重写dict,使之通过“.”调用
'''
def __setattr__(self, key, value):
print("In '__setattr__")
self[key] = value
def __getattr__(self, key):
try:
print("In '__getattr__")
return self[key]
except KeyError as k:
return None
def __delattr__(self, key):
try:
del self[key]
except KeyError as k:
return None
# __call__方法用于实例自身的调用,达到()调用的效果
def __call__(self, key): # 带参数key的__call__方法
try:
print("In '__call__'")
return self[key]
except KeyError as k:
return "In '__call__' error"
s = Dict()
print(s.__dict__)
# {}
s.name = "hello" # 调用__setattr__
# In '__setattr__
print(s.__dict__) # 由于调用的'__setattr__', name属性没有加入实例属性字典中。
# {}
print(s("name")) # 调用__call__
# In '__call__'
# hello
print(s["name"]) # dict默认行为
# hello
# print(s)
print(s.name) # 调用__getattr__
# In '__getattr__
# hello
del s.name # 调用__delattr__
print(s("name")) # 调用__call__