面向对象编程(下)

接着上一篇我们继续学吧!

一、一些内置函数

issubclass()

用于判断类对象与类对象之间的关系,传递两个参数:第一个实参是类对象,第二个实参是类对象或由类对象组成的元组。

class A(object):
    pass

class B(object):
    pass

class C(object):
    pass

class D(A):
    pass
  • 当第二个实参是类对象时,如果第一个实参是第二个实参的子类,那么返回True
print(issubclass(D, A))
print(issubclass(D, B))
#输出
True
False
  • 当第二个实参是类对象组成的元组时,如果第一个实参是第二个实参中任意一个类对象的子类,返回True
print(issubclass(D, (B, A, C)))
print(issubclass(D, (B, C)))
#输出
True #D是元组,是(B,A,C)中A的子类
False
isinstance()

用于判断实例对象与类对象之间的关系,接受两个实参:第一个实参是实例对象,第二个实参是类对象或由类对象组成的元组。

  • 当第二个实参是类对象时,如果第一个实参是第二个实参的实例对象,或者第一个实参是第二个实参的子类的实例对象,那么返回True
print(isinstance(D(),D))
print(isinstance(D(),A))
#输出
True
True
  • 当第二个实参是类对象组成的元组时,如果第一个实参是第二个实参中任意一个类对象或其子类的实例对象,返回True
print(isinstance(D(),(D,B,C)))
print(isinstance(D(),(B,A,C)))
#输出
True #A是D的子类,D()是D的实例
True #故D()是A的实例
type()

用于获得指定对象的类型,实例对象的类型是其对应的类对象,类自己的类型就是type

class MyClass(object):
    pass
    
mc = MyClass()
print(type(mc))
#输出
<class '__main__.MyClass'> #实例对象的类型为它的类对象

print(type(MyClass))
#输出
<class 'type'> #type是Python的一个总类,所有的类都属于在type中。因此一个类对象的type是type

print(type(int))
print(type(str))
#输出
<class 'type'>
<class 'type'>

def do_sth():
    pass
    
print(type(do_sth))
#输出
<class 'function'> #自定义函数的类型function

print(type(dir))
#输出
<class 'builtin_function_or_method'> #内置函数类型builtin_function_or_method

可以使用==判断某个对象的类型是否是指定的类型(自定义函数不是基本数据类型,不可这么判断)
对于基本数据类型,可以直接使用其对应的类名;如果不是基本数据类型,需要使用标准库中的模块types中定义的变量。

dir()

对于指定的类对象或实例对象,可以调用内置函数dir()获得其所有可以访问的实行和方法(包括从父类中继承的属性和方法)的列表。
类对象与实例对象的结果是有区别的,类对象的结果中不包括实例属性。
调用内置函数dir()后的返回值中,很多属性和方法都是以双下划线 __ 开头和结尾的,这些属性和方法中的绝大多数都继承自类object。
以双下划线 __ 开头和结尾的属性被称为特殊属性,以双下划线 __ 开头和结尾的方法被称为特殊方法。
特殊属性和方法都是系统预定义的,我们自定义的属性名和方法名不要以双下划线__开头和结尾。
在我们自定义类对象是,经常会重写一个或多个特殊方法,例如__init__。特殊方法会在特定的情形下被自动调用,很少会需要手动调用特殊方法。

二、属性之增删改查

  1. hasattr(object,name)
    用于判断指定的对象object是否有参数name指定的属性或方法。

  2. getattr(object,name[,default])
    用于获取指定的对象object中名为name的属性或方法。
    如果不指定参数default,那么当object中不存在名为name的属性和方法时,抛出AttributeError.
    如果指定了参数default,那么当object中不存在名为name的属性和方法时,返回default.
    getattr(object,name)等于:object.name

  3. setattr(object,name,value)
    用于在指定的对象object中添加或修改名为参数name的属性或方法,添加或修改后的值为value
    setattr(object,name,value)等价于:object.name = value

  4. delattr(object,name)
    用于删除指定的对象object中名为参数name的属性或方法。
    delattr(object,name)等价于:del object.name

注意:只有在不知道対象信息的情况下,オ会去获取対象的信息。因此,如果可以直接写: object. name,就不要写为getattr(object , name’)。

三、算术运算符重载

标准算术运算符在默认情况下不能用于自定义类对象的实例对象。如果想让标准算术运算符可以用于自定义类对象的实例对象,必须在自定义类对象中实现标准算术运算符对应的以下特殊方法:

  1. +对应的特殊方法是__ add__ ( )和_ radd_ ();
  2. -对应的特殊方法是_ sub__ ()和__ rsub__ ( ) ;
  3. *对应的特殊方法是__ mul__ ()和__ rmul__ ();
  4. /对应的特殊方法是 __ truediv__ ( )和__ rtruediv__ ( );
  5. //对应的特殊方法是__ floordiv__ ()和__ rfLoordiv__ ( )。

在这里插入图片描述

class MyClass1(object): 
	def __add__(self,other):
		return "这是__add__+的结果"
class MyClass2(object):
	def __radd__(self,other):
		return "这是__radd__+的结果" 
obj1 = MyClass1() 
obj2 = MyClass2() 
print(obj1+obj2)
#输出
这是__add__+的结果
class MyClass1(object): 
	pass 
class MyClass2(object): 
	def __radd__(self,other): 
		return "这是__radd__+的结果" 
obj1 = MyClass1() 
obj2 = MyClass2() 
print(obj1+obj2) 
#输出
这是__radd__+的结果
class MyClass1(object): 
	def __add__(self,other): 
		print("特殊方法__add__被调用") 
		return NotImplemented class MyClass2(object): 
		def __radd__(self,other): 
		return "这是__radd__+的结果" 
obj1 = MyClass1() 
obj2 = MyClass2() 
print(obj1+obj2) 
#输出
特殊方法__add__被调用 
这是__radd__+的结果

四、特殊方法

__ str __() 与 __ repr __()

类对象的特殊方法值__str__() 与 __ repr __()用于自定义并返回实例对象的字符串表示形式

  1. 当在交互式命令行中直接打印一个实例对象时,如果在实例对象对应的类对象中实现了特殊方法__ repr__ (), 会自动调用该方法;否则,会打印实例对象对应的类对象和实例对象在内存中的地址。
  2. 当调用内置函数print打印一个实例对象时,如果在实例对象对应的类对象中实现了特殊方法__ str__ (), 会自动调用该方法;
    否则,如果在实例对象对应的类对象中实现了特殊方法repr__ (), 会自动调用该方法;否则,会打印实例对象对应的类对象和实例对象在内存中的地址。
  3. 当调用内置函数str创建字符串并且实参是一个实例对象时,如果在实例对象对应的类对象中实现了特殊方法__ str__ (), 在内置函数str的内部会自动调用该方法; 否则,如果在实例对象对应的类对象中实现了特殊方法__repr__ (), 在内置函数st r的内部会自动调用该方法;否则,会打印实例对象对应的类对象和实例对象在内存中的地址。
  4. 当调用内置函数repr创建字符串并且实参是一一个实例对象时,如果在实例对象对应的类对象中实现了特殊方法__ repr__ (), 在内置函数repr的内部会自动调用该方法;否则,会打印实例对象对应的类对象和实例对象在内存中的地址。
class MyClass (object) :
	def__ str__ (self) :
		return”这是实例对象的字符串表示形式”

	__repr__=__ str__

通常情况下,类对象的特殊方法_ str__ ()和__repr__截图时隐藏当前窗口 现代码是一样的,因此,当实现了其中一个后,可以把其方法名赋值给另一个的方法名。

内置函数str( )和repr( )都返回对象的字符串表示,其区别在于:
str()的返回值是给用户看的,更加用户友好;
repr()的返回值是给程序开发者看的,是为调试服务的。

new()

当使用"类名( [实参] ) "创建实例对象时,Python解释 器的主要处理过程包括两大步:

  1. 调用特殊方法__ new___ ( )创建实例对象
    首先会查找该类对象是否实现了特殊方法__ new__ (), 如果没有实现,则去其父类中依次查找,直到找到类对象object。
  2. 调用特殊方法__ init__ ( )对创建的实例对象进行初始化
    __ new __ ( )返回的实例对象会作为实参被自动传递给__ init__ ()的第一 个形参self
class Parent(object): 
	def __new__(cls, *args, **kwargs): 
		pass 

class Child(Parent): 
	def __init__(self, name): 
		pass

id(Parent)
#输出
74270440
id(Child)
#输出
74278936
class Parent (object) : 
	def__ new__ (cls, *args, **kwargs) : 
		print("父类的__new__()被调用,其形参cls对应实参的id:",id(cls)) 
		obj = super().__ new__ (cls) 
		print ("创建的实例对象的id:", id(obj)) 
		return obj

class Child (Parent) :
      		def__ init__ (self, name) :
				print("子类的__init__()被调用,其形参self对应实参的id:"id(self))
				self. name = name

chile = Child("Mike")

#输出
父类的new__ () 被调用,其形参cls对应实参的id: 74269496
创建的实例对象的id: 90380272
子类的__ init_ () 被调用,其形参self对应实参的id:90380272	
gettitem(self,key)、setitem(key)、__delitem(self, key)

对于自定义类对象的实例对象,在默认情况下,是不能像列表和字典那样使用中括号来操作数据的。
如果想让自定义类对象的实例对象可以像列表和字典那样,使用中括号来操作数据。必须在自定义类对象中实现以下特殊方法:

  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__ ( )。
内置函数callable用于判断指定对象是否是可调用的。
除了函数对象是可调用的之外,对于实现了特殊方法__call__ ( )的类对象,其实例对象也是可调用的。

__ doc __ 与 __ dict __

调用内置函数dir得到的类对象的所有属性中,有一个特殊属性叫__ doc__, 用于表示类对象的文档字符串。
一、什么是类对象的文档字符串(docstring)
与函数的文档字符串类似,位于类对象的第一行的字符串被称为类对象的文档字符串,通常用三个引号表示。类对象的文档字符串是对类对象的功能的简要描述。
二、访问类对象的文档字符串
 通过类对象的特殊属性__doc__ 可以访问类对象的文档字符串。调用内置函数help( )得到的帮助信息中会包含类对象的文档字符串。

>>> class MyClass (object) :
			"""这是类对象的文档字符串
			1
			2
			3
			"""
			pass

>>> print (MyClass.__doc__)
> 这是类对象的文档字符串
		1
		2
		3

对于指定的类对象或实例对象,可以访问特殊属性__dict__ 获得该类对象或实例对象所绑定的所有属性和方法的字典。其中,字典中的键为属性名或方法名。

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__ ()。

五、特殊属性

Python是动态语言,所以,在创建对象之后,可以对其动态地绑定属性和方法。

如果想要对实例对象动态绑定的属性和方法的名称进行限制,可以在其对应的类对象中定义特殊属性__ slots__ 并给__ slots__ 赋值一个所有元素都为字符串的列表或元组,这样,对实例对象动态绑定的属性和方法的名称就只能来自于__slots__ 中的元素。

class MyClass(object):
	__slots__ = ("attr1", "do_sth1")

mc = MyClass()
mc.attr1 = 18
print(mc.attr1)
#输出
18
mc.attr2 = 56
#报错

def do_sth1(self):
	print("do_sth1被调用了")

from types import MethodType
mc.do_sth1 = MethodType(do_sth1, mc)

mc.do_sth1()
#输出
do_sth1被调用了

默认情况下,访问突例対象的属性是通过访问核突例対象的特殊属性__ dict__ 来实现的。例如:访问obj . x其实访问的是obj.dict [‘x’]
在类対象中定义了特殊属性__ slots__ 后, 其实例対象就不会在创建特殊属性__ dict__ 了 ,而是为每个属性创建一个描述器,访问属性吋就会直接调用这个描述器。调用描述器比访问__dict__ 要快,因此,在类対象中|定义特殊属性 __ slots __ 可 以提高属性的访问速度。
此外,在类対象中定义了特殊属性__slots__ 后, 由于其实例対象不再创建特殊属性__dict__ , 同肘,特殊属性__dict__ 是一个字典,字典的本质是哈希表,是一种用空囘換取时间的数据结构,因此,在类対象中定义特殊属性__slots__ 可以減少内存消耗。
特殊属性__slots__ 只対其所在类対象的实例対象起作用, 対其所在类対象的子类的实例対象是不起作用的。
如果子类也定义了特殊属性__slots__, 那么 子类的实例对象可以动态绑定的属性和方法的名称为子类的__slots__ 加上父类的__ slots__。

六、生成器与迭代器

生成器

列表推导式

[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 0x0000025C8F9019A8>

查看生成器对应的所有元素,有两种方式:

  1. 多次调用内置函数next(),每次调用都返回生成器的下一个元素,直到抛出异常StopIteration时表示没有更多元素了。
>>>a = (i * i for i in range(5))
>>>print(next(a))
0

>>>print(next(a))
1

>>>print(next(a))
4

>>>print(next(a))
9

>>>print(next(a))
16

>>>print(next(a))
StopIteration                             Traceback (most recent call last)
<ipython-input-31-94b12d3903f7> in <module>
----> 1 print(next(a))

StopIteration: 

2.使用for-in语句对生成器进行迭代,这样就不需要关心异常StopIteration了

a = (i * i for i in range(5))
for j in a:
    print(j)
---------------------------------
0
1
4
9
16

生成器中保存的并不是其对应的所有元素,而是如何推算出所有元素的算法。将生成器用于for-in语句时,元素是在循环的过程中不断被推送出来的。将生成器作为内置函数next()的实参时,返回的下一个元素也是在调用函数时被推送出来的。因此,生成器是惰性推算的,也就是说,只有当用到生成器中的某个元素时,才会临时进行推算,而并不会提前推算出来。

如果需要创建一个元素个数较大的容器,就可以考虑使用生成器,从而节约大量的存储空间。

生成器函数

上面使用类似生成式的语法得到的生成器被称为生成器表达式。此外,当推算的算法比较复杂时,还可以使用生成器函数得到生成器。
生成器函数中通过关键字yield返回推算出的元素。生成器函数与普通函数的区别在于:当调用内置函数next( )或使用for- in语句进行迭代时,执行完yield语句就会将生成器函数挂起,下次会从挂起的地方继续执行。

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) 
print(next(gf)) 
print(next(gf)) 
print(next(gf)) 
print(next(gf)) 
print(next(gf)) 
#输出
 1
 1
 2
 3
 5

遇到yield就挂起,往后运行yield后面的代码,遇到return生成器终止

迭代器

可以用于for-in语句的对象被称为可迭代(iterable)对象。例如:range、列表、元组、字符串、字典、集合、生成器,都是可迭代对象。可以调用内置函数isinstance()判断一个对象是否是可迭代对象。标准库模块collections中的类iterable用于表示可迭代对象。

from collections import Iterable print(isinstance([1,2,3],Iterable)) 
#输出
True 

print(isinstance('abc',Iterable)) 
#输出
True 

print(isinstance((i * i for i in range(1, 7)),Iterable)) 
#输出
True

如果一个可迭代对象可以作为内置函数next()的实参从而支持惰性推算,那么该对象被称为迭代器(Iterable)对象。
对于range、列表、元组、字符串、字典和集合等可迭代对象,都不可以作为内置函数next()的实参,而生成器可以。所以,生成器是迭代器的一种。
可以调用内置函数 isinstance() 判断一个对象是否是迭代器对象。标准库模块collections中的类Iterator用于表示迭代器对象。
在这里插入图片描述
可以调用内置函数 iter() 把不支持惰性推算的可迭代对象转换成迭代器对象
如果一个对象同时实现了特殊方法__iter__()和__next__(),那么该对象也被称为迭代器对象。如果将该对象用于 for-in 语句, for-in 语句首先会调用特殊方法 iter()返回一个可迭代对象,然后不断调用该可迭代对象的特殊方法__next__()返回下一次迭代的值,直到遇到StopIteration时退出循环。

class MyIterator(object):
    def __init__(self):
        self.data = 0
        
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.data > 5:
            raise StopIteration()
        else:
            self.data += 1
            return self.data


for item in MyIterator():
    print(item)
--------------------------------------------
1
2
3
4
5
6

for-in 语句在默认情况下不能用于自定义类对象的实例对象

若想让 for-in 语句可以用于自定义类对象的实例对象,必须在自定义类对象中实现特殊方法__iter__()和__next__()。 for-in语句首先会调用特殊方法__iter__()返回一个可迭代对象,然后不断调用可迭代对象的特殊方法__next__()返回下一次迭代的值,直到遇到StopIteration时退出循环。
只实现了特殊方法__iter__()的类对象,被称为可迭代类对象;同时实现了特殊方法__iter__()和__next__()的类对象,被称为迭代器类对象。
之所以 for-in 语句可以用于某些内置函数(如List、tuple、str等)的实例对象,是因为这些内置类对象中都同时实现了特殊方法__iter__()和__next__()。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值