python 6

一、Python repr()方法:显示属性

事实上,当使用该方法输出 Item 对象时,实际上输出的是 Item 对象的 repr() 方法的返回值。也就是说,下面两行代码的效果完全一样:

print (im)
print (im.__repr__)

二、Python __del__方法:销毁对象
当程序不再需要一个 Python 对象时,系统必须把该对象所占用的内存空间释放出来,这个过程被称为垃圾回收(GC,Garbage Collector),Python 会自动回收所有对象所占用的内存空间,因此开发者无须关心对象垃圾回收的过程。

Python 采用自动引用计数(ARC)方式来回收对象所占用的空间,
当程序中有一个变量引用该 Python对象时,Python 会自动保证该对象引用计数为 1;
当程序中有两个变量引用该 Python 对象时,Python 会自动保证该对象引用计数为 2
依此类推,如果一个对象的引用计数变成了 0,
 则说明程序
 中不再有变量引用该对象,表明程序不再需要该对象,因此 Python 就会回收该对象。

大部分时候,Python 的 ARC 都能准确、高效地回收系统中的每个对象。但如果系统中出现循环引用的情况,比如对象 a 持有一个实例变量引用对象 b,而对象 b 又持有一个实例变量引用对象 a,此时两个对象的引用计数都是 1,而实际上程序已经不再有变量引用它们,系统应该回收它们,此时 Python 的垃圾回收器就可能没那么快,要等专门的循环垃圾回收器(Cyclic Garbage Collector)来检测并回收这种引用循环。

===================================================================
当一个对象被垃圾回收时,Python 就会自动调用该对象的 del 方法。需要说明的是,不要以为对一个变量执行 del 操作,该变量所引用的对象就会被回收,
只有当对象的引用计数变成 0 时,该对象才会被回收。因此,如果一个对象有多个变量引用它,那么 del 其中一个变量是不会回收该对象的。

class Item:
    def __init__ (self, name, price):
        self.name = name
        self.price = price
    # 定义析构函数
    def __del__ (self):
        print('del删除对象')
# 创建一个Item对象,将之赋给im变量
im = Item('鼠标', 29.8)
x = im   # ①
# 打印im所引用的Item对象
del im
print('--------------')

程序中重写了 Item 类的 del() 方法,该方法就是 Item 类的析构函数,当系统将要回收 Item 时,系统会自动调用 Item 对象的 del() 方法。

上面程序先创建了一个 Item 对象,并将该对象赋值给 im 变量,① 号代码又将 im 赋值给变量 x,这样程序中有两个变量引用 Item 对象,接下来程序执行 del im 代码删除 im 对象,此时由于还有变量引用该 Item 对象,因此程序并不会回收 Item 对象。

运行上面程序,可以看到如下输出结果:

--------------
del删除对象

从上面程序的输出结果可以看到,del im 执行之后,程序并没有回收 Item 对象,只有等到程序执行将要结束时(系统必须回收所有对象),系统才会回收 Item 对象。

三、Python __dir__用法:列出对象的所有属性(方法)名

print(im.__dir__())  # 返回所有属性(包括方法)组成列表
print(dir(im))  # 返回所有属性(包括方法)===排序====之后的列表

四、Python __dict__属性:查看对象内部所有属性名和属性值组成的字典

五、setattr()、getattr()、hasattr()函数用法详解
在动态检查对象是否包含某些属性(包括方法〉相关的函数有如下几个:
**hasattr(obj, name):**检查 obj 对象是否包含名为 name 的属性或方法。
**getattr(object, name[, default]):**获取 object 对象中名为 name 的属性的属性值。
**setattr(obj, name, value,/):**将obj 对象的 name 属性设为 value。

class Comment:
    def __init__ (self, detail, view_times):
        self.detail = detail
        self.view_times = view_times
    def info ():
        print("一条简单的评论,内容是%s" % self.detail)
       
c = Comment('疯狂Python讲义很不错', 20)
# 判断是否包含指定的属性或方法
print(hasattr(c, 'detail')) # True
print(hasattr(c, 'view_times')) # True
print(hasattr(c, 'info')) # True
# 获取指定属性的属性值
print(getattr(c, 'detail')) # '疯狂Python讲义很不错'
print(getattr(c, 'view_times')) # 20
# 由于info是方法,故下面代码会提示:name 'info' is not defined
#print(getattr(c, info, '默认值'))
# 为指定属性设置属性值
setattr(c, 'detail', '天气不错')
setattr(c, 'view_times', 32)
# 输出重新设置后的属性值
print(c.detail)
print(c.view_times)

实际上 setattr() 函数还可对方法进行设置,在使用 setattr() 函数重新设置对象的方法时, 新设置的方法是未绑定方法。

def bar ():
    print('一个简单的bar方法')
# 将c的info方法设为bar函数   
setattr(c, 'info', bar)
c.info()

六、issubclass和isinstance函数:检查类型
Python 提供了如下两个函数来检查类型:
**issubclass(cls, class_or_tuple):**检查 cls 是否为后一个类或元组包含的多个类中任意类的子类。
**isinstance(obj, class_or_tuple):**检查 obj 是否为后一个类或元组包含的多个类中任意类的对象。

# str是object类的子类,输出True
print('str是否是object类的子类: ', issubclass(str, object))

my_list = [2, 4]
# [2, 4]是list类的实例,输出True
print('[2, 4]是否是list类的实例: ', isinstance(my_list, list))

七、__call__方法
我们用 hasattr() 函数判断指定属性(或方法)是否存在,但到底是属性还是方法,则需要进一步判断它是否可调用。程序可通过判断该属性(或方法)是否包含 call 属性来确定它是否可调用。

class User:
    def __init__(self, name, passwd):
        self.name = name
        self.passwd = passwd
    def validLogin (self):
        print('验证%s的登录' % self.name)       
        
u = User('crazyit', 'leegang')

# 判断u.name是否包含__call__方法,即判断是否可调用
print(hasattr(u.name, '__call__')) # False

# 判断u.passwd是否包含__call__方法,即判断是否可调用
print(hasattr(u.passwd, '__call__')) # False

# 判断u.validLogin是否包含__call__方法,即判断是否可调用
print(hasattr(u.validLogin, '__call__')) # True

对于程序中的函数,同样既可使用函数的语法来调用它,也可把函数当成对象,调用它的 call 方法。例如如下示例代码:

def foo ():
    print('--foo函数--')
# 下面示范了2种方式调用foo()函数
foo()
foo.__call__()

运行上面代码,可以看到 foo() 和 foo.call() 的效果完全相同。

八、getitemsetitemdelitemlen、__contains__用法

序列最重要的特征就是可包含多个元素, 因此和序列有关的特殊方法有如下几个:

len(self):该方法的返回值决定序列中元素的个数。

getitem(self, key):该方法获取指定索引对应的元素。该方法的 key 应该是整数值或 slice 对象,否则该方法会引发 KeyError 异常。

contains(self, item):该方法判断序列是否包含指定元素。

setitem(self, key, value):该方法设置指定索引对应的元素。该方法的 key 应该是整数值或 slice 对象,否则该方法会引发 KeyError 异常。

delitem(self, key):该方法删除指定索引对应的元素。

如果程序要实现不可变序列(程序只能获取序列中的元素,不能修改),只要实现上面前 3 个方法就行;

如果程序要实现可变序列(程序既能获取序列中的元素,也可修改),则需要实现上面
5 个方法。

九、 iter__和__reversed:实现迭代器

如果开发者需要实现迭代器,只要实现如下两个方法即可

iter(self):该方法返回一个迭代器(iterator),迭代器必须包含一个__next__()方法,该方法返回迭代器的下一个元素。

reversed(self):该方法主要为内建的 reversed() 反转函数提供支持,当程序调用 reversed() 函数对指定迭代器执行反转时,实际上是由该方法实现的。

定义一个代表斐波那契数列的迭代器

class Fibs:
    def __init__(self, len):
        self.first = 0
        self.sec = 1
        self.__len = len
        
    # 定义迭代器所需的__next__方法
    def __next__(self):
        # 如果__len__属性为0,结束迭代
        if self.__len == 0:
            raise StopIteration
        # 完成数列计算:
        self.first, self.sec = self.sec, self.first + self.sec
        # 数列长度减1
        self.__len -= 1
        return self.first
        
    # 定义__iter__方法,该方法返回迭代器
    def __iter__(self):
        return self
        
# 创建Fibs对象
fibs = Fibs(10)
# 获取迭代器的下一个元素
print(next(fibs))

# 使用for循环遍历迭代器
for el in fibs:
    print(el, end=' ')

上面程序定义了一个 Fibs 类,该类实现了 iter() 方法,该方法返回 self,因此它要求该类必须提供 next() 方法,该方法会返回数列的下二个值。程序使用 len 属性控制数列的剩余长度,当 __len 为 0 时,程序停止遍历。

上面程序创建了一个长度为 10 的数列,程序开始使用内置的 next() 函数来获取迭代器的下一个元素,该 next() 函数其实就是通过迭代器的 next() 方法来实现的。

程序接下来使用 for 循环来遍历应数列。运行该程序,将看到如下输出结果:

1 1 2 3 5 8 13 21 34 55

此外,程序可使用内置的 iter() 函数将列表、元组等转换成迭代器,例如如下代码:

将列表转换为迭代器

my_iter = iter([2, 'fkit', 4])
# 依次获取迭代器的下一个元素
print(my_iter.__next__()) # 2
print(my_iter.__next__()) # fkit

十、生成器详解

生成器和迭代器的功能非常相似,它也会提供 next() 方法,这意味着程序同样可调用内置的 next() 函数来获取生成器的下一个值,也可使用 for 循环来遍历生成器。

生成器与迭代器的区别在于,迭代器通常是先定义一个迭代器类,然后通过创建实例来创建迭代器;而生成器则是先定义一个包含 yield 语句的函数,然后通过调用该函数来创建生成器。

1.创建生成器
创建生成器需要两步操作:
1.定义一个包含 yield 语句的函数。
2.调用第 1 步创建的函数得到生成器。

def test(val, step):
    print("--------函数开始执行------")
    cur = 0
    # 遍历0~val
    for i in range(val):
        # cur添加i*step
        cur += i * step
        yield cur

yield cur 语句的作用有两点:
1.每次返回一个值,有点类似于 return 语句。
2.冻结执行,程序每次执行到 yield 语句时就会被暂停。

# 执行函数,返回生成器
t = test(10, 2)
print('=================')
# 获取生成器的第一个值
print(next(t)) # 0,生成器“冻结”在yield处
print(next(t)) # 2,生成器再次“冻结”在yield处

for ele in t:
print(ele, end=' ')
6 12 20 30 42 56 72 90

此外,程序可使用 list() 函数将生成器能生成的所有值转换成列表,也可使用 tuple() 函数将生成器能生成的所有值转换成元组。例如如下代码:

#再次创建生成器
t = test(10, 1)
#将生成器转换成列表
print (list (t))
#再次创建生成器
t = test(10, 3)
#将生成器转换成元组
print(tuple(t))

运行上面代码,可以看到如下输出结果:

--------函数开始执行------
[0 , 1 , 3, 6 , 10 , 15 , 21 , 28 , 36 , 45]
--------函数开始执行------
(0 , 3 , 9 , 18 , 30 , 45 , 63 , 84 , 108, 135)

2.生成器的方法

为了实现生成器与“外部程序” 动态地交换数据,需要借助于生成器的 send() 方法
对于上面详细的描述,归纳起来就是两句话:
1.外部程序通过 send() 方法发送数据。
2.生成器函数使用 yield 语句接收收据。

另外,需要说明的是,只有等到生成器**被“冻结”**之后,外部程序才能使用 send() 方法向生成器发送数据。获取生成器第一次所生成的值,应该使用 next() 函数;如果程序非要使用 send() 方法获取生成器第一次所生成的值,也不能向生成器发送数据,只能为该方法传入 None 参数

def square_gen(val):
    i = 0
    out_val = None
    while True:
        # 使用yield语句生成值,使用out_val接收send()方法发送的参数值
        out_val = (yield out_val ** 2) if out_val is not None else (yield i ** 2)
        # 如果程序使用send()方法获取下一个值,out_val会获取send()方法的参数
        if out_val is not None : print("====%d" % out_val)
        i += 1
sg = square_gen(5)

# 第一次调用send()方法获取值,只能传入None作为参数
print(sg.send(None))  # 0
print(next(sg))  # 1
print('--------------')

# 调用send()方法获取生成器的下一个值,参数9会被发送给生成器
print(sg.send(9))  # 81

# 再次调用next()函数获取生成器的下一个值
print(next(sg))  # 9

运行上面程序,可以看到如下输出结果:

0
1
--------------
====9
81
9

next=send(None)
send() 函数直接修改生成器返回值。即yield表达式的返回值。

此外,生成器还提供了如下两个常用方法:
1.close():该方法用于停止生成器。

程序就不能再去获取生成器的下一个值,否则就会引发异常。

2.throw():该方法用于在生成器内部(yield 语句内)引发一个异常。
在程序调用生成器的 throw() 方法引发异常之后,程序就会在 yield 语句中引发该异常。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值