【python学习笔记】第2天

继续今天的python学习,记录课堂重点。

'''
我们说到python是一门动态语言,可以在程序运行的过程中给类添加属性和方法,但我们有时候想限制它,让它
和其他静态语言一样,我们就要用到__slots__这样一个属性
'''

# 举例:

class Person(object):
    __slots__ = ("name") # __slots__限制我们只能添加名字为name的属性,在添加其他属性时会报错

p1 = Person()
p1.name = "Jany"
print(p1.name)
p1.age = 19
print(p1.age)

运行结果:

Traceback (most recent call last):
Jany
  File "C:/PycharmProjects/第二章/07___slots__的用法.py", line 14, in <module>
    p1.age = 19
AttributeError: 'Person' object has no attribute 'age'

可以看到,我们无法添加age这一属性。

 

接下来谈谈生成器

我们在存储数据的时候难免会遇到存储大量数据的时候,为了避免大量数据占用过多的内存空间,python中的生成器就可以解决这样的问题。

生成器有两种生成的方法,第一种就是将列表表达式中的“方括号”变为“圆括号”。

a = [i*2 for i in range(10)]
print(a)

运行结果:

[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

从结果可以看出,python中已经生成了有10个元素的列表,那么如果我们将10换为10000000会是什么样的结果呢?

读者自己测试(会很卡),那么在这10000000个元素当中,我们并不是每次都要用这么多的数据,浪费了大量的内存空间,于是我们用到了生成器。

a = (i*2 for i in range(10))
print(a)

运行结果:

<generator object <genexpr> at 0x000002420EFF2F48>

从结果可以看出,我们只是创建了一个生成器对象,在我们使用的时候才会变成我们想要的数据,那么我们究竟要如何把生成的数据给取出来呢?

答案就是用next()函数

print(next(a))
print(next(a))
print(next(a))

运行结果:

0
2
4

可以发现每一次调用next()函数都会取出一个数据,其实并不是取出数据,而是每一次调用时生成器会帮我们按照表达式来生成这个数据,这样就避免了内存空间占用过大的问题。

创建生成器的第二种方法是使用“yield”关键词

def test():
    for i in range(100):
        yield i

a = test()
print(a)
print(next(a))
print(next(a))
print(next(a))

运行结果:

<generator object test at 0x000002549A7C2F48>
0
1
2

注:当函数中有“yield”关键词的时候,此函数不是一个普通的函数,而是一个生成器。

利用__next__()方法也能达到同样的效果:

print(a.__next__())
print(a.__next__())
print(a.__next__())

运行结果:

0
1
2

再来看下面的例子:

def test():
    i = 0
    while i < 5:
        temp = yield i
        print(temp)
        i += 1

我们定义一个这样的函数,把yield i 的值赋给temp,然后打印出来会是什么样的结果呢?

a = test()
print(a.__next__())
print(a.__next__())
print(a.__next__())

运行结果:

0
None
1
None
2

为什么打印的是None?

当程序运行到yield i 的时候会自动暂停,然后接下来再运行的时候yield i是没有任何值的,所以只是将None赋值给了temp,为了解决这个问题我们引入了一个新的生成器方法:send()

print(a.__next__())
print(a.send("caca"))
print(a.send("wawa"))
print(a.send("gaga"))

运行结果:

0
caca
1
wawa
2
gaga
3

注意:第一次一定不能给生成器传值,否则会报错,读者不妨试试。

第一次除了用__next()__方法外还可以使用send(None)方法实现,send()方法的括号中需要一个参数,所以在这里我们必须要传入None

接下来再看一个例子:

print(a.send(None))
print(a.send("caca"))
print(a.__next__())
print(a.send("wawa"))
print(a.__next__())

运行结果:

0
caca
1
None
2
wawa
3
None
4

当我们在两个send()之间使用__next__()时,temp的值又变成了None,也就是说temp的值并没有被保存。

分析一下,temp在第一次执行yield i之后才开始接受yield i的值,也就是第二行的print(send("caca"))事实上是把“caca”赋值给了上一次的yield i然后再赋值给temp,而本次再执行到yield i时其值又变成了None,所以在下一次调用__next()__时,前一次 yield i 的值就是None

那么我们要如何永久性的保存temp的值呢?这里要将上面定义的函数做一个小小的改动:

def test():
    i = 0
    while i < 5:
        if i == 0:
            temp = yield i
            print(temp)
        else:
            yield i
            print(temp)
        i += 1

我们只给temp赋值一次,接下来我们只执行 yield i 

a = test()
print(a.send(None))
print(a.send("caca"))
print(a.__next__())
print(a.send("wawa"))
print(a.__next__())

运行结果:

0
caca
1
caca
2
caca
3
caca
4

这里我们也会发现一点,就是我们在执行第四次print的时候,“wawa”并没有被传入,temp的值永久性的保存了。

 

接下来看一下生成器如何实现完成多任务,原理就是多个生成器暂停然后再开启

def test1():
    while True:
        print("----1----")
        yield

def test2():
    while True:
        print("----2----")
        yield

t1 = test1()
t2 = test2()

while True:
    t1.__next__()
    t2.__next__()

运行结果:

请读者自行运行。。。

 

昨天学习了用函数来当装饰器装饰其他函数,接下来谈一下类当做装饰器。

class Test:
    def __init__(self,func):
        print("装饰器开始装饰...")
        print("The function name is %s" % func.__name__)
        self.__func = func

    def __call__(self):
        print("函数开始调用...")
        self.__func()


@Test
def test():
    print("函数已被调用...")

test()

运行结果:

装饰器开始装饰...
The function name is test
函数开始调用...
函数已被调用...

如果去掉test()这一行,就会打印出结果的前两行,说明装饰器在函数定义的时候就已经把函数给装饰了,其中@Test这一行相当于 test = Test(test) ,也就是说test指向了Test的实例对象,而其中的self.__func指向了原来的test()函数,所以在执行test()时会调用到__call__方法。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值