python-day4(字符串、列表、生成式和生成器、使用元组、集合、字典)

字符串和常用数据结构

简单用法

所谓字符串,就是由零个或多个字符组成的有限序列,一半记为s=a1a2a3…an(0<n<∞)。在python中,如果我们把单个或多个字符用单引号或者双引号包围起来,就可以表示一个字符串。

s1 = 'hello, world!'
s2 = "hello, world!"
# 以三个双引号或单引号开头的字符串可以拆行
s3 = """
hello,
world!
"""

print(s1, s2, s3, end='')

可以在字符串中使用 \ (反斜杠)来表示转义,也就是说 \ 后面的字符不再是它原来的意义,例如:\n不是代表反斜杠和字符n,而是表示换行;而 \t 也不是代表反斜杠和字符 t,而是表示制表符。如果想在字符串中表示 ’ 要写成 ',同理想要表示 \ 要写成 \。

s1 = '\'hello, world!\''
s2 = '\n\\hello, world!\\\n'
print(s1, s2, end='')
'hello, world!' 
\hello, world!\

在 \ 后面还可以跟一个八进制或者十六进制数来表示字符,例如 \141 和 \x61 都代表小写字母 a,前者时八进制的表示法,后者是十六进制的表示法。也可以在 \ 后面跟 Unicode 字符编码来表示字符,例如 \u8fbe\u6770 代表的是中文的”达杰“。

s1 = '\141\142\143\x61\x63'
s2 = '\u8fbe\u6770'
print(s1, s2, end='')
abcac 达杰

顺便提一句:Unicode编码的诞生和编码的”互用性"有关
如果不希望字符串中的 \ 表示转义,我们可以通过在字符串的最前面加上字母r来加以说明

s1 = r'\'hello, world!\''
s2 = r'\n\\hello,world!\\\n'
print(s1, s2, end='')
\'hello, world!\' \n\\hello,world!\\\n

python为字符串类型提供了丰富的运算符,我们可以使用 + 运算符来实现字符串的拼接,可以用 * 运算符来重复一个字符串的内容,可以使用 in 和 not in 来判断一个字符串是否包含另外一个字符串(成员运算),我们也可以用 [] 和 [:] 运算符从字符串取出某个字符或某些字符(切片运算)。


s1 = 'hello' * 3
print(s1)   # hello hello hello
s2 = 'world'
s1 += s2
print(s1)   # hello hello hello world
print('ll' in s1)   # True
print('good' in s1)     # False
str2 = 'abc123456'
# 从字符串中取出指定位置的字符(下标运算)
print(str2[2])  # c
# 字符串切片(从指定的开始索引到指定的结束索引)
"""简单切片"""
print(str2[2:5]) #c12
print(str2[-3:-1]) # 45
"""下标超出有效范围"""
print(str2[2:100])  # c123456
print(str2[-10:2])  # ab
"""缺省"""
print(str2[2:]) # c123456
"""缺省后面加步长"""
print(str2[2::2]) # c246
print(str2[::2]) # ac246
print(str2[::-1]) #645321cba

切片操作:

1、简单切片

a[start:stop]

其行为是得到下标在这样一个前闭后开区间范围内的元素,其中srart和stop为负数时,简单看作负数下标对应的位置即可。(下标是0开始的)

print(str2[2:5]) #c12
print(str2[-3:-1]) # 45

2、超出有效范围
当start或stop超出上文提到的有效范围时,切片操作不会抛出异常,而是进行截断。可以这样去理解截断机制:我们假象把索引范围扩充到全体整数,只不过小于或大于的区域对应空元素,在这个扩充后的数轴上进行切片,只需要把最终结果中的所有空元素忽略即可。

print(str2[2:100])  # c123456
print(str2[-10:2])  # ab

3、缺省
start和stop都是可以缺省的,在缺省的情况下,python的行为是尽可能取最大区间,具体来说:

按照补充索引范围的观点,start 的缺省值是无穷小(),stop的缺省是无穷大()

print(str2[2:]) # c123456

加步长的情况

print(str2[2::2]) # c246
print(str2[::2]) # ac246
print(str2[::-1]) #645321cba

后面那个数字为步长

其它对字符串的处理方法

str1 = 'hello,world!'
# 通过内置函数len计算字符串的长度
print(len(str1))    # 12
# 获得字符串首字母大写的拷贝
print(str1.capitalize())    # Hello,world!
# 获得字符串每个单词首字母大写的拷贝
print(str1.title())  # Hello,World!
# 获得字符串变大写后的拷贝
print(str1.upper())  # HELLO,WORLD!
# 从字符串中查找字串所在位置
print(str1.find('or'))  # 8
print(str1.find('shit'))    # -1
# 与find类似但找不到字串时会引发异常
# print(str1.index('or'))
# print(str1.index('shit'))
# 检查字符串是否以指定的字符串开头
print(str1.startswith('He'))    # False
print(str1.startswith('he'))    # True
# 检查字符串是否以指定的字符串结尾
print(str1.endswith('!'))   # True
# 将字符串以指定的宽度居中并在两次填充指定的字符
print(str1.center(50, '*'))
# 将字符串以指定的宽度靠右放置左侧填充指定的字符
print(str1.rjust(50, ' '))
str2 = 'abc123456'
# 检查字符串是否由数字构成
print(str2.isdigit())   # False
# 检查字符串是否由字母构成
print(str2.isalpha())   # False
# 检查字符串是否以字母和数字构成
print(str2.isalpha())   # True
str3 = ' 1391595889@qq.com'
print(str3)
# 获得字符串修剪左右两侧空格后的拷贝
print(str3.strip())

输出结果如下:

12
Hello,world!
Hello,World!
HELLO,WORLD!
7
-1
False
True
True
*******************hello,world!*******************
                                      hello,world!
False
False
False
 1391595889@qq.com
1391595889@qq.com

格式化输出字符串

a, b = 5, 10
print('%d * %d = %d' % (a, b, a * b))

c, d = 6, 9
print('{0} * {1} = {2}'.format(c, d, c * d))

e, f = 7, 8
print(f'{e} * {f} = {e * f}')

输出结果

5 * 10 = 50
6 * 9 = 54
7 * 8 = 56

使用列表

刚才我们讲到的字符串类型( str ) 和之前讲到的数值类型 ( int 和 float )有一些区别。数值类型时标量类型,也就说这种类型的对象没有可以访问的内部结构;而字符串类型是一种结构化的、非标量类型,所以才会有一系列的属性和方法。接下来我们要介绍的列表( list ) 也是一种结构化的、非标量类型,它是值的有序序列,每个值都可以通过索引进行标识,定义列表可以将列表的元素放在 [ ] 中,多个元素用,进行分隔,可以用 for 循环对列表元素进行遍历,也可以用 [ ] 或 [:]运算符取出列表中的一个或多个元素
下面代码演示了如何定义列表、如何遍历列表以及列表的下标运算。

list1 = [1, 3, 5, 7, 100]
print(list)     # [1, 3, 5, 7, 100]
# 称号表示列表元素的重复
list2 = ['hello'] * 3
print(list2)    # ['hello', 'hello', 'hello']
# 计算列表长度(元素个数)
print(len(list1))   # 5
# 下标(索引)运算
print(list1[0])     # 1
print(list1[4])     # 100
# print(list1[5]))      # indexError: list index out of range
print(list[-1])     # 100
print(list1[-3])    # 5
list1[2] = 300
print(list1)    # [1, 3, 300, 7, 100]
# 通过循环用下标遍历列表元素
for index in range(len(list1)):
    print(list1[index])
# 通过for循环遍历列表元素
for elem in list1:
    print(elem)
# 通过enumerate函数处理列表之后再遍历可以同时获得元素索引和值
for index, elem, in enumerate(list1):
    print(index, elem)

输出:

['hello', 'hello', 'hello']
5
1
100
list[-1]
5
[1, 3, 300, 7, 100]
1
3
300
7
100
1
3
300
7
100
0 1
1 3
2 300
3 7
4 100

下面代码演示了如何向列表添加元素以及如何从列表中移除元素

list1 = [1, 3, 5, 7, 100]
# 添加元素
list1.append(200)
list1.insert(1, 400)
# 合并两个列表
# list1.extend([1000, 2000])
list1 += [1000, 2000]
print(list1)    # [1, 400, 3, 5, 7, 100, 200, 1000, 2000]
# 从指定的位置删除元素
list1.pop(0)
list1.pop(len(list1) - 1)
print(list1)    # [1, 400, 5, 7, 100, 200, 1000, 2000]
# 清空列表元素
list1.clear()
print(list1)    # []

和字符串一样,列表也可以进行切片操作,通过切片操作我们可以实现对列表的复制或者将列表中的一部分取出来创建出新的列表

fruits = ['grape', 'apple', 'strawberry', 'waxberry']
fruits += ['pitaya', 'pear', 'mango']
# 列表切片
fruits2 = fruits[1:4]
print(fruits2)  # apple strawberry waxberry
# 可以通过完整切片操作来复制列表
fruits3 = fruits[:]
print(fruits3)   # ['grape', 'apple', 'strawberry', 'waxberry', 'pitaya', 'pear', 'mango']
fruits4 =  fruits[-3:-1]
print(fruits4)  # ['pitaya', 'pear']
# 可以通过反向切片操作来获得倒转后的列表的拷贝
fruits5 = fruits[::-1]
print(fruits5)  # ['mango', 'pear', 'pitaya', 'waxberry', 'strawberry', 'apple', 'grape']

下面代码实现了对列表的排序操作

list1 = ['orange', 'apple', 'zoo', 'intetnationalization', 'blueberry']
list2 = sorted(list1)
# soreted函数返回列表排序后的拷贝不会修改传入的列表
# 函数的设计就应该向sorted函数一样尽可能不产生副作用
list3 = sorted(list1, reverse=True)
# 通过key关键字参数指定根据字符串长度进行排序而不是默认的字母表顺序
list4 = sorted(list1, key=len)
print(list1)
print(list2)
print(list3)
print(list4)
# 给列表对象发出排序消息直接在列表对象上进行排序
list1.sort(reverse=True)
print(list1)

描述:
sorted()函数对所有可迭代的对象进行排序操作

sort与sorted的区别:
sort是应用在list上的方法,sorted可以对所有可迭代的对象进行排序操作
list的sort方法的是对已经存在的列表进行操作,无返回值,而内建函数sorted方法返回的是一个新的list,而不是在原来的基础上进行操作

语法

sorted(iterable, cmp=None, key=None, reverse=False)

参数说明:

iterable – 可迭代对象。
cmp – 比较的函数,这个具有两个参数,参数的值都是从可迭代对象中取出,此函数必须遵守的规则为,大于则返回1,小于则返回-1,等于则返回0。
key – 主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序。
reverse – 排序规则,reverse = True 降序 , reverse = False 升序(默认)。

生成式和生成器

我们还可以使用列表的生成式语法来创建列表,代码如下:

f = [x for x in range(1,10)]
print(f)
f = [x + y for x in 'ABCDE' for y in '1234567']
print(f)
# 用列表的生成表达式语法创建列表容器
# 用这种语法创建列表之后元素已经准备就绪所以需要耗费较多的内存空间
f = [x ** 2 for x in range(1, 1000)]
print(sys.getsizeof(f))     # 查看对象占用内存的字节数
print(f)
# 请注意下面的代码创建的不是一个列表而是一个生成器对象
# 通过生成器可以获取到数据但是它不占用额外的空间存储数据
# 每次需要数据的时候就通过内部的运算得到数据(需要花费额外的时间)
f = (x ** 2 for x in range(1, 1000))
print(sys.getsizeof(f))     # 相比生成式生成器不占用存储数据的空间
print(f)
for val in f:
    print(val)

除了上面提到的生成器语法,python中还有另外一种定义生成器的方式,就是通过yield关键字将一个普通函数改造成生成器函数。下面代码演示了如何实现一个生成斐波那契数列的生成器。

def fib(n):
    a, b = 0, 1
    for _ in range(n):
        a, b = b, a + b
        yield a
        
def main():
    for val in fib(20):
        print(val)

if __name__ == '__main__':
    main()

简单地讲,yield的作用就是把一个函数变成一个generator,带有yield的函数不再是一个普通函数,python解释器会将其视为一个generator,调用fib(20)不会执行fib函数,而是返回一个iterable对象,在for循环执行时,每次循环都会执行fib函数内部的代码,执行到 yield a 时,fib函数就返回一个迭代值,下次迭代时,代码从 yield a 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。

使用元组

python中的元组与列表类似也是一种容器数据类型,可以用一个变量(对象)来存储多个数据,不同之处在于元组的元素不能修改。我们把多个元素组合到一起就形成了一个元组,所以它和列表一样可以保存多条数据。下面代码演示了如何定义和使用元组。

# 定义元组
t = ('陈达杰', 25, True, '江西宜春')
print(t)
# 获取元组中的元素
print(t[0])
print(t[3])
# 遍历元组中的值
for member in t:
    print(member)
# 重新给元组赋值
# t[0] = '奥特曼'      # TypeErro
# 遍历t重新引用了新的元组原来的元组将被垃圾回收
t = ('奥特曼', 8000, True, 'M78星云')
print(t)
# 将元组转换为列表
person = list(t)
print(person)
# 列表是可以修改它的名字的
person[0] = '李小龙'
person[1] = 25
print(person)
# 将列表转换为元组
fruits_list = ['apple', 'banana', 'orange']
fruits_tuple = tuple(fruits_list)
print(fruits_tuple)

【注】为什么我们已经有了列表这种数据结构,还需要元组这样的类型?
1、元组中的元素是无法修改的,事实上我们在项目中尤其是多线程环境中可能更喜欢使用的是那些不变对象(一方面因为对象状态不能修改,所以可以避免由此引起的不必要的程序错误,简单的说就是一个不变的对象要比可变的对象更加容易维护;另一方面因为没有任何一个线程能够修改不变对象的内部状态,一个不变对象自动就是线程安全的,这样就可以省掉处理同步化的开销,一个不变对象可以方便的被共享访问)。所以结论就是:如果不需要对元素进行添加、删除、修改的时候,可以考虑使用元组,当然如果一个方法要返回多个值,使用元组也是不错的选择。
2、元组在创建时间和占用的空间上面都优于列表,我们可以使用sys模块的getsizeof函数来检查存储同样的元素的元组和列表各自占用了多少内存空间,这个很容易做到。我们也可以在python中使用魔法指令%timeit来分析创建同样内容的元组和列表所花费的时间。(宁愿相信自己是麻瓜也不相信这个世界没有魔法!)

使用集合

python中的集合和数学上的集合是一致的,不允许有重复元素,而且可以进行交集、并集、差集等运算。
在这里插入图片描述
可以按照下面代码所示方式来创建和使用集合

# 创建集合的字面量语法
set1 = {1, 2, 3, 3, 2}
print(set1)
print('Length = ', len(set1))
# 创建集合的构造器语法
set2 = set(range(1, 10))
set3 = set((1, 2, 3, 3, 2, 1))
print(set2, set3)
# 创建集合的推导式语法(推导式也可以用于推导集合)
set4 = {num for num in range(1, 100) if num % 3 == 0 or num % 5 == 0}
print(set4)

输出为:

{1, 2, 3}
Length =  3
{1, 2, 3, 4, 5, 6, 7, 8, 9} {1, 2, 3}
{3, 5, 6, 9, 10, 12, 15, 18, 20, 21, 24, 25, 27, 30, 33, 35, 36, 39, 40, 42, 45, 48, 50, 51, 54, 55, 57, 60, 63, 65, 66, 69, 70, 72, 75, 78, 80, 81, 84, 85, 87, 90, 93, 95, 96, 99}

向集合添加元素和从集合删除元素

# 向集合添加元素和从集合删除元素
set1.add(4)
set1.add(5)
set2.update([11, 12])
set2.discard(5)
if 4 in set2:
    set2.remove(4)
print(set1, set4)
print(set3.pop()) 	# pop() 方法用于随机移除一个元素。
print(set3)

说明:python中允许通过一些特殊的方法来为某种类型或数据结构自定义运算符,上面的代码中我们对集合进行运算的时候可以调用集合对象的方法,也可以直接使用对应的运算符,例如& 运算符和Intersection方法的作用是一样的,但是让代码更加直观

使用字典

字典是另一种可变容器模型,python中的字典跟我们生活中使用的字典是一样的,它可以存储任意类型对象,与列表、集合不同的是,字典的每个元素都是由一个键和一个值组成的“键值对”,键和值通过冒号分开,下面代码演示了如何定义和使用字典。

# 创建字典的字面量语法
scores = {'陈达杰': 98, '奥特曼': 99, '章北海': '100'}
print(scores)
# 创建字典的构造语法
items1 = dict(one=1, two=2, three=3, four=4)
# 通过zip函数将两个序列压成字典
items2 = dict(zip(['a', 'b', 'c'], '123'))
# 创建字典的推导式语法
items3 = {num: num ** 2 for num in range(1, 10)}
print((items1, items2, items3))
# 通过键可以获取字典中对应的值
print(scores['陈达杰'])
print(scores['奥特曼'])
# 对字典中所有键值对进行遍历
for key in scores:
    print(f'{key}: {scores[key]}')
# 更新字典中的元素
scores['奥特曼'] = 90  # 这是对奥特曼的削弱!
scores['诸葛王朗'] = 71
scores.update(冷面=67, 李寻欢=85)
print(scores)
if '武则天' in scores:
    print(scores['武则天'])
print(scores.get['武则天', 60])
# 删除字典中的元素
print(scores.popitem())
print(scores.popitem())
print(scores.pop('陈达杰', 100))
# 清空字典
scores.clear()
print(scores)

练习

1、在屏幕上显示跑马灯文字

import os
import time

def main():
    content = '厦门欢迎我,欢迎欢迎我......'
    while True:
        # 清理屏幕上的输出
        os.system('cls')    # os.system('clear')
        print(content)
        # 休眠200毫秒
        time.sleep(0.2)
        content = content[1:] + content[0]

if __name__ == '__main__':
    main()
content = content[1:] + content[0]

这条代码是不断迭代跑马灯上的几个字的,和切片操作联系起来。所以,编程逻辑是需要多看多写的。
2、设计一个函数产生指定长度的验证码,验证码由大小写字母和数字构成

import random

def generate_code(code_len=4):
    """
    生成指定长度的验证码
    :param code_len: 验证码的长度(默认4个字符)
    :return: 由大小英文字母和数字构成的随机验证码
    """
    all_chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'    # 定义验证码的内容
    last_pos = len(all_chars) - 1   # last_pos = 字符串长度减一,因为下标从0开始
    code = ''   # 验证码初始为空
    for _ in range(code_len):   # 循环 code_len 次
        index = random.randint(0, last_pos)     # 随机生成一个下标
        code += all_chars[index]    # 将该下标上的字符赋值给要生成的验证码
    return code     # 返回验证码

练习3、设计一个函数返回给定文件名的后缀名

def get_suffix(filename, has_dot=False):
    """
    获取文件名的后缀名

    :param filename: 文件名
    :param has_dot: 返回的后缀名是否需要带点
    :return: 文件的后缀名
    """

    pos = filename.rfind('.')       # 从右往左获取 . 的位置参数
    if 0 < pos < len(filename) - 1:     # 只要能找到,返回的值一定大于 -1
        index = pos if has_dot else pos + 1     # 三元运算符,如果 has_dot 为 True 那么返回 pos, 如果为 False 则返回 pos + 1
        return filename[index:]     # 返回后缀
    else:       # 没找到,返回空文件串
        return ''
"""
Python rfind() 返回字符串最后一次出现的位置,如果没有匹配项则返回 -1。
str.rfind(str, beg=0 end=len(string))   # 从字符串右边向左边寻找
"""

一个是三元运算符,一个是.rfind函数。python的函数真挺方便的,也不知道自己有没有机会能去研究一些更加深奥的东西,逝者如斯夫。
4、设计一个函数返回传入的列表中最大和第二大的元素的值

def max2(x):
    m1, m2 = (x[0], x[1]) if x[0] > x[1] else (x[1], x[0]) # 三目运算符,将较大值赋值给m1,第二大值赋值给m2
    for index in range(2, len(x)):      # 从列表的第二个元素开始遍历
        if x[index] > m1:   # 如果x[index] > m1
            m2 = m1     # 将 m1 的值赋值给 m2
            m1 = x[index]       # 将x[index]的值赋值给 m1
        elif x[index] > m2:     # 如果x[index]的值大于 m2但是不大于 m1
            m2 = x[index]       # 将x[index]的值赋值给 m2
    return m1, m2       # 返回m1,m2

计算指定的年月日是这一年的第几天

def is_leap_year(year):
    """
    判断指定的年份是不是闰年

    :param year: 年份
    :return: 闰年返回True平年返回False
    """
    return year % 4 == 0 and year % 100 != 0 or year % 400 == 0

def which_day(year, month, date):
    """
    计算传入的日期是这一年的第几天

    :param year: 年
    :param month: 月
    :param date: 日
    :return: 第几天
    """

    days_of_month = [
        [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
        [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
    ][is_leap_year(year)]       # 列表后面跟一个判断条件,倒是第一次见(喂!你也没学几天python啊!)
    total = 0
    for index in range(month - 1):
        total += days_of_month[index]
    return total + date

之前按自己的思路写的话,肯定每个月份每个月份都写出来了,果然看别人的代码有助于自己养成好的习惯
练习6、打印杨辉三角形
在这里插入图片描述

def main():
    num = int(input('Number of rows: '))    # 输入打印出多少行
    yh = [[]] * num     # 申请num个列表
    for row in range(len(yh)):      # 行循环
        yh[row] = [None] * (row + 1)    # 第一行一个,第二行2个,第三行3个。。。
        for col in range(len(yh[row])):     # 列循环
            if col == 0 or col == row:      # 如果是yh[row][0]或yh[row][row],元素的值为1
                yh[row][col] = 1
            else:   # 其它位置元素的值为当前元素肩上两元素的和
                yh[row][col] = yh[row - 1][col] + yh[row - 1][col - 1]
            print(yh[row][col], end='\t')
        print()

if __name__ == '__main__':
    main()

输出结果:

Number of rows: 5
1	
1	1	
1	2	1	
1	3	3	1	
1	4	6	4	1	

值得注意的是,先要定义多少行,然后再考虑杨辉三角形各个元素之间的关系。(对于一个好的程序,也要考虑它所占用的内存空间)
综合案例1、双色球选号

"""
双色球选号
双色球投注区分为红球号码区和蓝球号码区,红球号码范围为01~33,蓝球号码范围为01~16。
双色球每期从33个红球中开出6个号码,从16个蓝球中开出1个号码作为中奖号码,双色球玩法
即是竞猜开奖号码的6个红球号码和1个蓝球号码,顺序不限

Version: 0.1
Author: 陈达杰
"""

"""
sample(序列a,n) 功能:从序列a中随机抽取n个元素,并将n个元素生以list形式返回。
enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。
enumerate(sequence, [start=0])  sequence -- 一个序列、迭代器或其他支持迭代对象 start -- 下标起始位置的值。

"""
from random import randrange, randint, sample

def display(balls):
    """
    输出列表中的双色球号码
    """
    for index, ball in enumerate(balls):    # 遍历balls列表
        if index == len(balls) - 1:     # 将红球和蓝球用|分隔开
            print('|', end=' ')
        print('%02d' % ball, end=' ')       # 将随机选出来的号码打印出来
    print()

def random_select():
    """
    随机选择一组号码
    """
    red_balls = [x for x in range(1, 34)]   # 红球随机选择1~34中的号码
    selected_balls = []     # 将它们定义成一个列表
    selected_balls = sample(red_balls, 6)       # 从列表中随机选择6个号码
    selected_balls.sort()       # 将它们排列
    selected_balls.append(randint(1, 16))   # 在列表中添加一个蓝球
    return selected_balls   # 返回

def main():
    n = int(input('机选几注: '))
    for _ in range(n):
        display(random_select())

if __name__ == '__main__':
    main()

综合案例2、约瑟夫环

"""
约瑟夫环
约瑟夫问题是个著名的问题:N个人围成一圈,第一个人从1开始报数,报M的将被杀掉,
下一个人接着从1开始报。如此反复,最后剩下一个,求最后的胜利者。

Version: 0.1
Author: 陈达杰
"""

def main():
    persons = [True] * 30   # 首先,每个人都是幸运儿
    counter, index, number = 0, 0, 0
    while counter < 15:     # 如果人数小于15时,继续砂仁!
        if persons[index]:
            number += 1     # 不断报数
            if number == 9:     # 把报数为9的人砂惹!
                persons[index] = False      # 沙雕以后,把它定义为 False
                counter += 1    # 砂一个,记一个
                number = 0      # 沙雕以后要重新开始报数
        index += 1      # 往后遍历
        index %= 30     # 30人是一个循环
    for person in persons:      # 把幸存者和被砂的都打印出来
        print('基' if person else '非', end='')

if __name__ == '__main__':
    main()

约瑟夫环有以下几个重点:
1、跳出循环的条件是什么
2、大家的初始状态是什么
3、如何处理报数问题
4、当遇到标识数时如何重新计数
要学会砂仁猪心!
综合案例3、井字棋游戏

"""
井字棋
英文名叫Tic-Tac-Toe,是一种在3*3格子上进行的连珠游戏,和五子棋类似,由于棋盘一般不画边框,
格线排成井字故得名。游戏需要的工具仅为纸和笔,然后由分别代表O和X的两个游戏者轮流在格子里留下
标记(一般来说先手者为X),任意三个标记形成一条直线,则为获胜。

Version: 0.1
Author: 陈达杰
"""

import os

def print_board(board):
    print(board['TL'] + '|' + board['TM'] + '|' + board['TR'])
    print('_+_+_+')
    print(board['ML'] + '|' + board['MM'] + '|' + board['MR'])
    print('_+_+_+')
    print(board['BL'] + '|' + board['BM'] + '|' + board['BR'])

def main():
    init_board = {
        'TL': ' ', 'TM': ' ', 'TR': ' ',
        'ML': ' ', 'MM': ' ', 'MR': ' ',
        'BL': ' ', 'BM': ' ', 'BR': ' '
    }
    begin = True
    while begin:
        curr_board = init_board.copy()
        begin = False
        turn = 'x'
        counter = 0
        os.system('clear')
        print_board(curr_board)
        while counter < 9:
            move = input('轮到%s走棋, 请输入位置: ' % turn)
            if curr_board[move] == ' ':
                counter += 1
                curr_board[move] = turn
                if turn == 'x':
                    turn = 'o'
                else:
                    turn = 'x'
            os.system('clear')
            print_board(curr_board)
        choice = input('再玩一局?(yes|no)')
        begin = choice == 'yes'

if __name__ == '__main__':
    main()

井字棋这个,好像有点问题,等我周末再看看。本章学了挺久,代码还是写少了看少了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值