随机生成姓名

  1. 需要把“姓”与“名”分为两组常用字,所以可以定义两个列表;
  2. 需要从两个列表中随机抽取一个字,于是可以使用 random.sample;
  3. 需要让每一次 sample 取出的字数都不固定,在一个字和两个字之间随机确定,因此可以将其参数指定为 randint(1,2);
  4. 需要让名字列表中出现重复文字,从而实现“叠字”姓名,因此可以使用列表乘法;
  5. 需要从文件中将全体常用字读入列表,因此可以使用 fopen 或 xlwings 等方法。
  1. from random import sample, randint
  2. = ['赵', '钱', '孙', '李']
  3. = ['慧', '芝', '雅', '琳', '秋', '月', '虹', '云']
  4. = * 2
  5. 名单 = []
  6. while len(名单) < 20:
  7. 人物 = ''.join(sample(姓, 1) + sample(名, randint(1, 2)))
  8. if 人物 not in 名单:
  9. 名单.append(人物)
  10. print(名单)
  1. ['钱月', '孙云', '赵虹秋', '李云芝', '孙琳', '赵琳', '钱琳云', '钱慧', '钱芝月', '孙虹', '赵雅云', '李雅琳', '赵琳云', '赵雅', '孙慧', '钱云雅', '钱琳', '孙芝虹', '孙虹雅', '赵月芝']

控制 while 循环
将计数器更新操作( i+=1 )放到判断语句中,只有在成功添加新姓名后才更新计数器,从而确保生成的姓名总数(而while本身的执行次数则变得不确定)。

可迭代对象(iterable)

zip对象

zip()函数在执行合并操作后,返回的并不是一个列表,而是一个zip对象。
zip对象属于一种 “可迭代对象” (实际上是“可迭代对象”中的“迭代器(iterator)”,轮流出列,逐个访问)

迭代(iterate)

按某种顺序逐个访问

所有容器类型的对象,都是可迭代对象,都支持for循环这种迭代过程
可迭代对象不仅包括列表等各种容器,还包括range、zip等其他各种类型

  1. = ['张', '林', '令狐', '王', '周', '洪', '东方', '黄']
  2. = ['三丰', '平之', '冲', '重阳']
  3. 门派 = ['武当', '华山', '华山', '全真']
  4. 名单 = zip(门派, 姓, 名)
  5. for n in 名单:
  6. print(n)
  1. ('武当', '张', '三丰')
  2. ('华山', '林', '平之')
  3. ('华山', '令狐', '冲')
  4. ('全真', '王', '重阳')
  1. = ['张', '林', '令狐', '王', '周', '洪', '东方', '黄']
  2. = ['三丰', '平之', '冲', '重阳']
  3. 门派 = ['武当', '华山', '华山', '全真']
  4. 名单 = zip(门派, 姓, 名)
  5. for n in 名单:
  6. print(''.join(n))
  1. 武当张三丰
  2. 华山林平之
  3. 华山令狐冲
  4. 全真王重阳

zip对象用法与列表类似,是否完全一致?

  1. print(名单[2])
  1. ---------------------------------------------------------------------------
  2. TypeError Traceback (most recent call last)
  3. <ipython-input-20-c0f1ee5a101d> in <module>
  4. ----> 1 print(名单[2])
  5. TypeError: 'zip' object is not subscriptable
  1. print(len(名单))
  1. ---------------------------------------------------------------------------
  2. TypeError Traceback (most recent call last)
  3. <ipython-input-21-b7fba21236b8> in <module>
  4. ----> 1 print(len(名单))
  5. TypeError: object of type 'zip' has no len()

zip 对象不能像列表那样使用 a[i] 的方式访问指定位置的元素

  1. x = range(10)
  2. x
  1. range(0, 10)
  1. type(x)
  1. range

range()返回的结果不是一个列表(list 对象),而是一个 range 对象
但 range 类型也是“可迭代类型”,因此也可用for循环访问
如果需要 list 对象,还需用 list() 函数将 range 对象转换成 list
为什么这样设计?

  1. x = list(range(1000000000))
  2. print(x[257])
  3. # 会卡死
  4. # list会生成完整的列表放到内存里
  5. x_ = range(1000000000)
  6. print(x_[257])
  7. # 正常运行

在Python程序中创建一个列表对象,Python会首先划分出足够的内存并填入所有数据。

如果数据量太大,有可能造成内存不足导致错误; 而如果创建同样内容的 range 对象,则只消耗极少内存,无论数据量大小。

练习

练习1:zip基本操作

编写Python程序、或直接在Python交互式命令窗口中,逐次完成下列各项任务:

  1. 定义三个列表,分别为 x=[1,2,3],y=[‘a’,’b’,’c’],z=[‘甲’,’乙’,’丙’] 。
  2. 使用zip函数将三个列表合并,顺序为 (’甲’,1,’a’) ,结果赋值给变量 a 。
  3. 使用 for 循环逐个输出 a 中的内容,即每个元组。
  4. 让列表 x 和 y 等于自身乘以2,然后观察两个列表的变化。
  5. 再次执行第2步中的zip操作并将结果赋值给 a ,然后观察a是否变化,思考原因。
  6. 思考与试验:如果将第 1 步中的 x=[1,2,3] 改为 x=range(3) ,是否能够成功执行 zip 操作?
  1. x=[1,2,3]
  2. y=['a','b','c']
  3. z=['甲','乙','丙']
  4. a = zip(z, x, y)
  5. for i in a:
  6. print(i)
  1. ('甲', 1, 'a')
  2. ('乙', 2, 'b')
  3. ('丙', 3, 'c')
  1. x=[1,2,3]
  2. y=['a','b','c']
  3. x = x * 2
  4. y = y * 2
  1. x
  1. [1, 2, 3, 1, 2, 3]
  1. y
  1. ['a', 'b', 'c', 'a', 'b', 'c']
  1. a = zip(z, x, y)
  2. for i in a:
  3. print(i)
  1. ('甲', 1, 'a')
  2. ('乙', 2, 'b')
  3. ('丙', 3, 'c')
  1. x=range(3)
  2. y=['a','b','c']
  3. z=['甲','乙','丙']
  4. a = zip(z, x, y)
  5. for i in a:
  6. print(i)
  1. ('甲', 0, 'a')
  2. ('乙', 1, 'b')
  3. ('丙', 2, 'c')

练习2

首先,我们已经从互联网上找到了一些姓名常用字,并保存到了文本文件中,即 常见姓氏,男生姓名常用字 和 女生姓名常用字。

编写一个程序,由用户通过input语句指定 “男生/女生” 和 名字个数,然后按要求自动生成姓名。

  1. from random import sample, randint
  1. lastname = []
  2. fname_boy = []
  3. fname_girl = []
  4. lastname_file = open('python02_13_01_xing.txt', 'r', encoding = 'utf8')
  5. fname_boy_file = open('python02_13_01_boy.txt', 'r', encoding = 'utf8')
  6. fname_girl_file = open('python02_13_01_girl.txt', 'r', encoding = 'utf8')
  1. for i in lastname_file.readlines():
  2. for each in i:
  3. if each not in [" ", "\n"]:
  4. lastname.append(each)
  5. for j in fname_boy_file.readlines():
  6. for each in j:
  7. if each not in [" ", "\n", "、"]:
  8. fname_boy.append(each)
  9. for k in fname_girl_file.readlines():
  10. for each in k:
  11. if each not in [" ", "\n", "、"]:
  12. fname_girl.append(each)
  13. lastname_file.close
  14. fname_boy_file.close
  15. fname_girl_file.close
  1. def auto_name(gender, num_name):
  2. auto_name_boy = []
  3. auto_name_girl = []
  4. if gender == '男':
  5. while len(auto_name_boy) < int(num_name):
  6. name_boy = ''.join(sample(lastname, 1) + sample(fname_boy, randint(1, 2)))
  7. if name_boy not in auto_name_boy:
  8. auto_name_boy.append(name_boy)
  9. return auto_name_boy
  10. else:
  11. while len(auto_name_girl) < int(num_name):
  12. name_girl = ''.join(sample(lastname, 1) + sample(fname_girl, randint(1, 2)))
  13. if name_girl not in auto_name_girl:
  14. auto_name_girl.append(name_girl)
  15. return auto_name_girl
  16. gender = input('请输入性别:')
  17. num_name = input('请输入数量')
  18. print(auto_name(gender, num_name))
  1. 请输入性别:女
  2. 请输入数量4
  3. ['戈素欢', '蒋月岚', '燕婉', '芮仪']

思路

对于 “由用户指定性别” 的要求,可以将用户的输入(’男’/‘女’)赋值给某个变量(比如 gender),
然后使用 if 语句对其判断,根据其内容将不同的文本文件名(boy.txt或girl.txt)赋值给某个变量比如 name_file 。
这样接下来使用 fopen(name_file) 即可正确打开所需的名字文件。

对于从文本文件中读入常用字到列表,可以使用下面的代码,通过字符串对象的split方法将读入的每一行(对应一个字符串)拆分为一个列表,并且要注意删除空格、空行、空串等无意义的字符:

  1. xing = []
  2. all_txt = []
  3. with open('d:/python_02_13_01_xing.txt', encoding='utf-8') as f:
  4. all_txt = f.readlines()
  5. for s in all_txt:
  6. xing.extend( s.strip().split(' ') )
  7. print(xing)

【注意】如果是读入本例中的名字列表,则需要以顿号 “ 、” 为分隔符。