文章目录
__ new __()
当使用"类名( [实参] ) "创建实例对象时,Python解释 器的主要处理过程包括两大步:
1.调用特殊方法__ new___ ( )创建实例对象
首先会查找该类对象是否实现了特殊方法__ .new__ (), 如果没有实现,则去其父类中依次查找,直到类对象object。
2.调用特殊方法_ init_ ( )对创建的实例对象进行初始化
__ new __ ( )返回的实例对象会作为实参被自动传递给_ init_ ()的第一 个形参self。
>>> class Parent (object) :
def __new__ (cls, *args,**kwargs) :
pass
>>> class Chi ld (Parent):
def__ init_ (self, name) :
pass
>>> id(Parent)
> 74270440
>>> ud(Child)
> 74278936
>>> class Parent (object) :
def__ new__ (cls, *args, **kwargs) :
print("父类的__new__()被调用,其形参cls对应实参的id:",id(cls))
id(c1s))obj = super0._ new__ (c1s)
print ("创建的实例对象的id:", id(obj))
return obj
>>> class Child (Parent) :
def__ init__ (self, name) :
print("子类的__new__()被调用,其形参cls对应实参的id:"id(self))
self. name = name
>>> child = Child(Mike")
> 父类的new__ () 被调用,其形参cls对应实参的id: 74269496创建的实例对象的id: 90380272
子类的__ init_ () 被调用,其形参self对应实参的id:90380272
__ getitem ( )、 setitem __()、 __ delite __()
对于自定义类对象的实例对象,在默认情况下,是不能像列表和字典那样使用中括号语法来操作数据的。
如果想让自定义类对象的实例对象可以像列表和字典那样,使用中括号语法来操作数据,必须在自定义类对象中实现以下特殊方法:
1.__ getitem__ (self, key)
当执行操作obj [key]时,会自动调用该特殊方法。
2. __ setitem __ (self, key, value)
当执行操作obj [key] = vaLue时, 会自动调用该特殊方法。
3. __ delitem __ (self, key)
当执行操作del obj [key]时,会自动调用该特殊方法。
>>> class MyDict (object):
def_ init__ (self):
self. data = {}
def__ getitem__ (self, key):
return self. data[key]
def__ setitem__ (self, key, value) :
self. data[key] = value
def__ delitem__ (self, key):
del self. data [key]
>>> md = MyDict()
>>> md[" one"] = 18
>>> md["two"] = 32
>>> print (md. data)
{'one' : 18,'two':32}
__ call __( ) 与 callable()
如果在类对象中实现了特殊方法call_ (), 那么就可以像调用函数-样直接调用这个类对象的实例对象,从而会自动调用特殊方法_ call_ ( )。
>>> class MyClass (object) :
def_ ,call__ (self, *args, *kwargs) :
print (args, kwargs)
>>> mc = MyClass()
>>> mc()
> () {}
>>> mc(1,2,x=3,y=4)
> (1, 2) {'x': 3,y': 4}
内置函数calLable用于判断指定对象是否是可调用的。
除了函数对象是可调用的之外,对于实现了特殊方法_ call__ ( )的类对象,其实例对象也是可调用的。
__ doc __ 与 __ dict __
调用内置函数dir得到的类对象的所有属性中,有一-个特殊属性叫__ doc_, 用于表示类对象的文档字符串。
一、什么是类对象的文档字符串(docstring)
与函数的文档字符串类似,位于类对象的第一 行的字符串被称为类对象的文档字符串,通常用三个引号表示。类对象的文档字符串是对类对象的功能的简要描述。
二、访问类对象的文档字符串
通过类对象的特殊属性_ doc_ 可以访问类对象的文档字符串。调用内置函数help( )得到的帮助信息中会包含类对象的文档字符串。
>>> class MyClass (object) :
"""这是类对象的文档字符串
1
2
3
"""
pass
>>> print (MyClass._ _doc_)
> 这是类对象的文档字符串
1
2
3
对于指定的类对象或实例对象,可以访问特殊属性_ dict 获得 该类对象或实例对象所绑定的所有属性和方法的字典。其中,字典中的键为属性名或方法名。
>>> class MyClass (object):
ca="ca"
def_ init__ (self):
self.ia = "ia
def im(self):
pass
@classmethoddef cm(cls):
pass
@stati cmethod
def sm() :
pass
>>> MyClass. ca2 = "ca2"
>>> MyClass._ _dict__
mappingproxy({'__ module__' : '_ main__',
ca':' ca',
'__init__ ': <function __main__ . MyClass. __ init__ (self)>,
'im':<function__main__. MyClass. im(self)>,
'cm': <classmethod at 0x56292b0>,
'sm' : Kstati cmethod at 0x5629ac8>,
'__dict__': <attribute '__ dict__' of ' MyClass' objects>
'__weakref__' :<attribute '__ weakref__ ' of ' MyClass' objects>
'__doc__' : None,
ca2: 'ca2 '})
>>> mc = MyClass()
>>> mc. ia2 ="ia2"
>>> print(mc._ dict__)
> {'ia' : 'ia', 'ia2' : 'ia2' }
特殊属性之 __ slots __
Python是动态语言,所以,在创建对象之后,可以对其动态地绑定属性和方法。
如果想要对实例对象动态绑定的属性和方法的名称进行限制,可以在其对应的类对象中定义特殊属性__ slots__ 并给__ slots__ 赋值- 个所有元素都为字符串的列表或元组,这样,对实例对象动态绑定的属性和方法的名称就只能来自于__slots__ 中的元素。
默认情况下,访问突例対象的属性是通过访问核突例対象的特殊属性__ dict__ 来实现的。例如:访问obj . x其实访问的是obj._dict [‘x’]
在类対象中定义了特殊属性__ slots__ 后, 其实例対象就不会在创建特殊属性__ dict__ 了 ,而是为每个属性创建一个描述器,访问属性吋就会直接调用这个描述器。调用描述器比访问__dict__ 要快,因此,在类対象中|定义特殊属性 __ slots __ 可 以提高属性的访问速度。
此外,在类対象中定义了特殊属性__slots__ 后, 由于其实例対象不再创建特殊属性__dict__ , 同肘,特殊属性__dict__ 是一个字典,字典的本质是哈希表,是一种用空囘換取时间的数据结构,因此,在类対象中定义特殊属性__slots__ 可以減少内存消耗。
特殊属性__slots__ 只対其所在类対象的实例対象起作用, 対其所在类対象的子类的实例対象是不起作用的。
如果子类也定义了特殊属性__slots__, 那么 子类的实例对象可以动态绑定的属性和方法的名称为子类的__slots__ 加上父类的__ slots__。
特殊方法之 __ len() __
内置函数Len ( )用于返回对象的长度。
内置函数Len ()的实参在默认情况下不能是自定义类对象的实例对象。
>>> print(len([1,2, 3, 4, 5]))
> 5
>>> print(len( abcde' ))
> 5
>>> print(len(a': 1, 'b': 2,'c': 3]))
> 3
如果想让内置函数Len( )的实参可以是自定义类对象的实例对象,必须在自定义类对象中实现特殊方法__len__ ()。 这样,调用内置函数len()时,在其内部会自动调用实参所对应类对象的特殊方法__len__ ()。 之所以内置函数len() 的实参可以是上述内置类对象的实例对象,是因为上述的内置类对象中都实现了特殊方法__len__ ()。
生成器和迭代器
生成器(generater)
>>> [i * i for i in range(8)] #列表推导式
> [0, 1, 4, 9, 16, 25, 36, 49]
>>> (i * i for i in range(8)) #生成器表达式
> <generator object <genexpr> at 0x000001FA6DFD7FC0>
>>> a = (i * i for i in range(8)) #生成器是一次性的
>>> for i in a:
print(i)
> 4
9
16
25
36
49
生成器函数
上面使用类似生成式的语法得到的生成器被称为生成器表达式。此外,当推算的算法比较复杂时,还可以使用生成器函数得到生成器。
生成器函数中通过关键字yield返回推算出的元素。生成器函数与普通函数的区别在于:当调用内置函数next( )或使用for- in语句进行迭代时,执行完yield语句就会将生成器函数挂起,下次会从挂起的地方继续执行。
>>> def fib(n):
i = 0
a, b = 1,1
while i < n:
print(a,end = ',')
a,b = b,a+b
i += 1
>>> fib(6)
> 1,1,2,3,5,8,
>>> def fib(n):
i = 0
a, b = 1,1
while i < n:
yield a
a,b = b,a+b
i += 1
>>> fib(6)
> <generator object fib at 0x000001FA6E0C3468>
>>> next(fib(6))# 运行一次重启一次
> 1 #不管重复多少次运行,结果都是1
>>> gf = fib(6) #遇到yield就挂起,第二次运行yield后面的代码
>>> print(next(gf))
>>> print(next(gf))
>>> print(next(gf))
>>> print(next(gf))
>>> print(next(gf))
> 1
1
2
3
5
迭代器(iterator)
可以用于for-in语句的对象被称为可迭代(iterable)对象。
例如:range、列表、元组、字符串、字典、集合、生成器,都是可迭代对象。
可以调用内置函数isinstance()判断一个对象是否是可迭代对象。标准库模块collections中的类iterable用于表示可迭代对象。
能用内置函数next()调用的才是iterator