python生成器→杨辉三角→深浅拷贝

实训需要,最近一直都在学python,刚起步吧,很多没接触到的语言新特性觉得蛮新奇的。然后在看生成器的时候发现了这个问题,在此记录下。

杨辉三角:

题意:

廖雪峰老师有一道题是:
把杨辉三角每一行看做一个list,试写一个generator,不断输出下一行的list:

# 期待输出:
# [1]
# [1, 1]
# [1, 2, 1]
# [1, 3, 3, 1]
# [1, 4, 6, 4, 1]
# [1, 5, 10, 10, 5, 1]
# [1, 6, 15, 20, 15, 6, 1]
# [1, 7, 21, 35, 35, 21, 7, 1]
# [1, 8, 28, 56, 70, 56, 28, 8, 1]
# [1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
n = 0
results = []
for t in triangles():
    print(t)
    results.append(t)
    n = n + 1
    if n == 10:
        break
if results == [
    [1],
    [1, 1],
    [1, 2, 1],
    [1, 3, 3, 1],
    [1, 4, 6, 4, 1],
    [1, 5, 10, 10, 5, 1],
    [1, 6, 15, 20, 15, 6, 1],
    [1, 7, 21, 35, 35, 21, 7, 1],
    [1, 8, 28, 56, 70, 56, 28, 8, 1],
    [1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
]:
    print('测试通过!')
else:
    print('测试失败!')

我们要实现的就是triangles()这个生成器。

不完美代码:

def triangles():
    L = [1]
    while True:
        yield L
        L.append(0)
        L = [L[i - 1] + L[i] for i in range(len(L))]

解析:

1、杨辉三角第一行只有一个1,所以先建立一个L = [1],第一次调用的时候直接返回L即可。
2、第二行及以后的思路,根据杨辉三角的特点“每行每个数字等于上一行该位置的相邻两个数字之和”,所以对每一行都可以用

newList[0] = L[i - 1] + L[i]

这种逻辑去处理,而且不需要用一个大list去装载每一行的list,可以每次都用列表生成式重构这个list。
那么问题来了,边界的1咋办,它们上一行没有两个相邻数字,这里列出两种方法:
①、先算出除了首尾的1以外的所有数,最后再在首尾补1即可。
②、没有条件就创造条件,给上一行首尾补0。
即比如[1, 2, 1]可以看成[0, 1, 2, 1, 0],
这样下一行就为[1, 3, 3, 1]。
python中list[-1]是倒数第一个元素,对newList[0] = list[-1] + list[0]处理的时候完全不用担心list[-1]边界溢出的问题,所以完全可以把第一个0删掉,这样就是每算一行list,就在该行list末尾补0。而且巧妙的是,杨辉三角下一行确实比上一行要只多一个数。这就是L.append(0)的原因了。

总结一下代码的思路:生成器每次都从yield L语句后继续运行,所以我们对L末尾补0,这样新的L长度刚好是新的一行杨辉三角的长度,在range(len(L))这个长度范围内对每个数字都进行newL[i]=L[i - 1] + L[i]处理即可。

改进与思考:

问题1:

L = [L[i - 1] + L[i] for i in range(len(L))]
这行代码在运行的时候,例如 L = [1,2,1,0]生成[1,3,3,1],生成第一个3之后,原来的2应该被覆盖了。
答:我觉得应该是[L[i - 1] + L[i] for i in range(len(L))]这里面会隐式地生成一个newList,这个newList内容与L一模一样,是L的深拷贝。所以不会立即改动到L,然后等代码逻辑全跑完才返回该newList赋值给L,此时L才发生改变。

问题2:

不完美代码运行,显示的是对的,但是测试显示失败。

看上面测试的代码,输出“测试失败”也就是result的值是错的。
输出results一看,果然,results每一行末尾都带着一个0。
[[1, 0],
[1, 1, 0],
[1, 2, 1, 0],
[1, 3, 3, 1, 0],
[1, 4, 6, 4, 1, 0],
[1, 5, 10, 10, 5, 1, 0],
[1, 6, 15, 20, 15, 6, 1, 0],
[1, 7, 21, 35, 35, 21, 7, 1, 0],
[1, 8, 28, 56, 70, 56, 28, 8, 1, 0],
[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]]
然而最后一行末尾没有带0,所以回去看triangles生成器的代码:

for t in triangles():
    print(t)
    results.append(t)
    n = n + 1
    if n == 10:
        break

每次print(t)的时候t还是正确的,但是执行results.append(t)的时候,由于results.append(t)的参数t是引用传递,所以此时results这个大list中的小list和t指向的List是同一个地址。
而我们再次执行到for t in triangles():的时候,triangles()里面从yield L处继续运行,马上就会执行L.append(0),因而使得刚才的t指向的List末尾多了一个0。
因此不完美代码还是有瑕疵的,应该怎么修改呢?

完美代码:

①、利用浅拷贝,改为

def triangles():
    L = [1]
    while True:
        yield L
        L = L.copy()
        L.append(0)
        L = [L[i - 1] + L[i] for i in range(len(L))]

②、可以把L.append(0)改成L = L + [0],L.append(0)是直接在原来的地址指向的L的末尾加0,而 L + [0]只对当前变量修改,不会修改到原来的L。

def triangles():
    L = [1]
    while True:
        yield L
        L = L + [0]
        L = [L[i - 1] + L[i] for i in range(len(L))]

深拷贝与浅拷贝:

区别

  • 直接赋值(等号):其实就是对象的引用(别名)。
  • 浅拷贝(copy):拷贝父对象,不会拷贝对象的内部的子对象(子对象仍然是引用状态)。
  • 深拷贝(deepcopy): copy 模块的 deepcopy 方法,完全拷贝了父对象及其子对象(可以尽情修改也不会影响到原来的)。

示意图

赋值引用

b = a: 赋值引用,a 和 b 都指向同一个对象。
这里写图片描述

浅拷贝

b = a.copy(): 浅拷贝, a 和 b 是一个独立的对象,但他们的子对象还是指向统一对象(是引用)。
这里写图片描述

深拷贝

b = copy.deepcopy(a): 深度拷贝, a 和 b 完全拷贝了父对象及其子对象,两者是完全独立的。
这里写图片描述

实例:

#!/usr/bin/python
# -*-coding:utf-8 -*-

import copy
a = [1, 2, 3, 4, ['a', 'b']] #原始对象

b = a                       #赋值,传对象的引用
c = copy.copy(a)            #对象拷贝,浅拷贝
d = copy.deepcopy(a)        #对象拷贝,深拷贝

a.append(5)                 #修改对象a
a[4].append('c')            #修改对象a中的子数组对象

print( 'a = ', a )
print( 'b = ', b )
print( 'c = ', c )
print( 'd = ', d )

输出为

(‘a = ‘, [1, 2, 3, 4, [‘a’, ‘b’, ‘c’], 5])
(‘b = ‘, [1, 2, 3, 4, [‘a’, ‘b’, ‘c’], 5])
(‘c = ‘, [1, 2, 3, 4, [‘a’, ‘b’, ‘c’]])
(‘d = ‘, [1, 2, 3, 4, [‘a’, ‘b’]])

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 杨辉三角可以用 Python 来生成,代码如下: def generate_pascal(num_rows): triangle = [] for count in range(num_rows): row = [None for _ in range(count+1)] row[0], row[-1] = 1, 1 for j in range(1,len(row)-1): row[j] = triangle[count-1][j-1] + triangle[count-1][j] triangle.append(row) return triangle print(generate_pascal(5)) 答案:可以使用 Python 来生成杨辉三角,代码如下:def generate_pascal(num_rows): triangle = [] for count in range(num_rows): row = [None for _ in range(count+1)] row[0], row[-1] = 1, 1 for j in range(1,len(row)-1): row[j] = triangle[count-1][j-1] + triangle[count-1][j] triangle.append(row) return triangle print(generate_pascal(5)) ### 回答2: 杨辉三角是指每个数字是上方两个数字的和的等腰三角形排列。Python可以通过使用循环和列表来生成杨辉三角。 我们可以首先创建一个空列表来存储杨辉三角的每一行。然后,使用嵌套的循环来计算并添加每个数字到列表中,直到生成所需的行数。 下面是一个用Python生成杨辉三角的示例代码: ```python def generate_triangle(n): triangle = [] # 创建一个空的列表来存储每一行的数字 for i in range(n): row = [] # 创建一个空的列表来存储当前行的数字 for j in range(i + 1): if j == 0 or j == i: row.append(1) # 每一行的第一个和最后一个数字都为1 else: row.append(triangle[i - 1][j - 1] + triangle[i - 1][j]) # 其他数字是上方两个数字的和 triangle.append(row) # 将当前行添加到三角形列表中 return triangle rows = int(input("请输入行数:")) triangle = generate_triangle(rows) for row in triangle: print(row) ``` 这段代码首先定义了一个名为`generate_triangle`的函数,接受一个整数参数`n`,表示要生成的杨辉三角的行数。在函数中,我们使用两个嵌套的循环来计算和添加每个数字到列表中。外部循环`i`控制行数,内部循环`j`控制每行的数字数量。 在内部循环中,我们首先检查当前数字是否为该行的第一个或最后一个数字。如果是,我们将数字设置为1,否则,我们通过索引访问上一行的两个数字并相加,将和添加到当前行。 最后,我们将当前行添加到三角形列表中。循环结束后,返回生成的杨辉三角列表。 在主程序中,我们首先接受用户输入的行数,然后调用`generate_triangle`函数生成杨辉三角。最后,我们使用循环打印每一行的数字。 运行程序时,用户将被要求输入行数,然后程序将生成并打印相应行数的杨辉三角。 ### 回答3: 杨辉三角是一个由数字构成的三角形,数字是根据一定规律生成的。使用Python可以很方便地生成杨辉三角。 在Python中,我们可以使用列表和循环来生成杨辉三角。我们可以定义一个二维列表或者使用嵌套的一维列表来表示杨辉三角的每一行。 首先,我们可以定义一个空列表来存储杨辉三角。然后,我们使用循环来迭代生成每一行的数字。在每一行中,首先插入1作为起始元素,然后根据上一行的元素计算出当前行的元素。具体的计算方式是,当前行的第i个元素等于上一行的第i-1个元素和第i个元素的和。最后,在每一行的末尾插入1作为结束元素。 下面是一个示例代码: ```python def generate_triangle(rows): triangle = [] for i in range(rows): row = [1] if i > 0: for j in range(1, i): row.append(triangle[i-1][j-1] + triangle[i-1][j]) row.append(1) triangle.append(row) return triangle # 生成6行的杨辉三角 rows = 6 triangle = generate_triangle(rows) # 打印生成的杨辉三角 for row in triangle: print(row) ``` 运行以上代码,将会得到类似以下的输出: ``` [1] [1, 1] [1, 2, 1] [1, 3, 3, 1] [1, 4, 6, 4, 1] [1, 5, 10, 10, 5, 1] ``` 这就是用Python生成的杨辉三角。我们可以通过调整`rows`的值来生成不同行数的杨辉三角
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值