【python】python入门基础理论+实践,这一篇就够了

简介:第一个hello world

print("hello world!");

for i in range(10):
    print('run:',i);

import time;
time.sleep(10);

字符串str

在Python中,加了引号的字符都被认为是字符串!

name = "zhangsan"

多引号

多引号什么作⽤呢?作⽤就是多⾏字符串必须⽤多引号

message = '''
这是多行的字符串,
换行了哈,
再一次换行了.
'''
print(message)

字符串拼接

字符串只能进⾏”相加”和”相乘”运算。

name = "zhangsan"
hobbie = 'football'

info = name  + hobbie
print(info) # zhangsanfootball

allname = name * 2
print(allname) # zhangsanzhangsan

字符串的拼接只能是双⽅都是字符串,不能跟数字或其它类型拼接。

name = "zhangsan"
age = 20
hobbie = 'football'
print(type(name)),print(type(age)) #<class 'str'> <class 'int'>

# 打印多行信息 python2.x 语法
# %s 字符串占位符
# %d 数字占位符
# %f 浮点数占位符
msg = '''
-------- %s info ----------
name: %s
age: %d
hobbie: %s   
------------end ------------
''' % (name,name,age,hobbie)
print(msg)


# 打印多行信息python3.x 语法
# 添加 f 解析{} 占位符内容
msg2 = f'''
-------- {name} info ----------
name: {name}
age: {age}
hobbie: {hobbie}   
------------end ------------
'''
print(msg2)

字符串方法

格式化方法

a = 'tom'
a.capitalize() # Tom 首字母大写

b = 'Zhang San'
b.casefold() # zhang san  为了方便字符串对比,把字符串都转化为小写

c = 'jack center'
# string.center() 字符串两边填充
c.center(50,'-') # -------------------jack center--------------------

d = 'Hello\tworld' # 使用默认制表符大小(8个空格) 
d1 = 'Hello\tworld\tfrom\tPython' 
d2 = "Hello world"

r1 = d1.expandtabs(4) # 指定制表符大小为4个空格 
r2 = d2.expandtabs() # 没有制表符的字符串
# print(d)
# print(r1)
# print(r2)

a2 = 'left string'
a2 = a2.ljust(40,'-') # 字符串从左边根据长度开始填充
# print(a2) # left string-----------------------------

a3 = 'right string'
a3 = a3.rjust(40,'-') # 字符串从右边根据长度开始填充
# print(a3) # ----------------------------right string

a4 = 'Tom Jack'
a4 = a4.lower() # 全变小写
# print(a4) # tom jack

a5 = 'Tom Jack'
a5 = a5.swapcase() # 大小写互换
# print(a5) # tOM jACK

a6 = 'hello world'
a6 = a6.title() # 改成标题,即每个单词首字母大写
# print(a6) # Hello World

a7 = 'hello world'
a7 = a7.upper() # 改大写
# print(a7) # HELLO WORLD

a8 = 'hello world'
a8 = a8.zfill(50) # 字符串空的地方填0
# print(a8) # 000000000000000000000000000000000000000hello world

a9 = ' hello world \n '
# print(a9) #  hello world 
a9 = a9.strip() # 去掉两边的空格换行符
# print(a9) #hello world

a10 = ' hello world'
a10 = a10.lstrip() # 去掉左边的空格
# print(a10)  # hello world

a11 = ' hello world'
a11 = a11.rstrip() # 去掉右边的空格
# print(a11) #  hello world

a12 = 'my name is {name}, i am {age} old.'
a12 = a12.format(name = 'Tom',age = 28) # 引用外部变量
# print(a12) # my name is Tom, i am 28 old.

# format 使用方式二
a13 = 'my name is {0}, i am {1} old.'
a13 = a13.format('Tom', 28)
# print(a13) # my name is Tom, i am 28 old.

判断方法

a1 = 'hello world'
r1 = a1.startswith('he')
# print(r1) # True

a2 = 'hello world'
r2 = a2.endswith('ld')
# print(r2) # True

a3 = 'hello world'
r3 = a3.isalpha() # 是不是字母,不能包括空格
# print(r3) # False

a4 = 'zhangsan25'
r4 = a4.isalnum() # 是不是字符or数字
# print(r4) # True

a5 = '123'
r5 = a5.isdigit() # 是不是数字,不能有小数点
# print(r5) # True
# print('12.3'.isdigit()) # False

a6 = '2name'
r6 = a6.isidentifier() # 是不是合法的变量名字
# print(r6) # False

a7 = 'Tom'
r7 = a7.islower() # 是否是小写
# print(r7) # False

a8 = '26'
r8 = a8.isnumeric() # 判断是不是数字,中文的数字描述也能判断
# print(r8) # True
# print('二十六'.isnumeric()) # True


a9 = 'hello'
r9 = a9.isprintable() # 是否能打印,比如图片资源就不能被打印
# print(r9) # True

a10 = ' '
r10 = a10.isspace() # 是不是空格
# print(r10) # True

a11 = 'The Title'
r11 = a11.istitle() # 判断是不是标题(首字母大写)
# print(r11) # True

a12 = 'THE TITLE'
r12 = a12.isupper() # 是不是都是大写
print(r12) # True

查、改、计数、替换

a1 = 'hello world'
r1 = a1.find('l') # 查找字符串第一次出现的索引,找不到结果会返回-1
# print(r1) # 2

a2 = 'hello world'
r2 = a2.rfind('l') # 从右侧开始查找字符串第一次出现的索引
# print(r2) # 9

a3 = 'hello world'
r3 = a3.index('l') # 查找字符串的索引,跟find的区别是,index找不到会报错
# print(r3) # 2

a4 = 'hello world'
r4 = a4.rindex('l') # 从右侧开始查找字符串第一次出现的索引,跟rfind的区别是,rindex找不到索引会报错
# print(r4) # 9
# print(a4.rindex('H')) # 找不到报错了

a5 = 'hello world'
r5 = a5.count('l') # 字符串出现的次数
# print(r5) # 3

a6 = 'hello world'
r6 = a6.split() # 默认以空格切分,并返回列表
# print(r6) # ['hello', 'world']

a7 = 'hello world python'
r7 = a7.rsplit() # 从右边开始切,默认看不出区别
# print(r7) # ['hello', 'world', 'python']
# print(a7.rsplit(maxsplit = 1)) # ['hello world', 'python'] 意思是从右边切一下,形成了2个列表的元素

a8 = 'hello\nworld\npython'
r8 = a8.splitlines() # 根据\n分割字符串形成列表
# print(r8) # ['hello', 'world', 'python']

a9 = 'hello world'
r9 = a9.removeprefix('he') # 移除从前面匹配的字符串
# print(r9) # llo world

a10 = 'hello world'
r10 = a10.removesuffix('ld') # 移除从后边开始匹配的字符串
# print(r10) # hello wor

a11 = 'hello world'
r11 = a11.replace('hello','pyton') # 替换字符串
print(r11) # pyton world
print(a11.replace('l','T',2)) #  heTTo world  把l替换成T,并且指定替换的次数为2

特殊方法

names = ['zhangsan','lisi','tom']
r1 = '-'.join(names) # 把列表转成字符串方法
# print(r1) # zhangsan-lisi-tom

source = 'abcdefghi' # 数据源
output = '012345678' # 对等加密输入
password_table = str.maketrans(source,output) # 生成密码本
msg = 'hello world'
r2 = msg.translate(password_table) # 加密
print(r2) # 74llo worl3

字符串文本加密示例:

import string
source = string.printable 
# print(source) # 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ 
output = string.printable[::-1] # 反转
# print(output) # ~}|{`_^]\[@?>=<;:/.-,+*)('&%$#"!ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba9876543210

password_table = str.maketrans(source,output) # 输出一个密码本
msg = 'hello world' # 需要加密的原文
encrypt_msg = msg.translate(password_table) # 加密后的内容
# print(encrypt_msg) # ?\;;.5&.+;]

password_table2 = str.maketrans(output,source) # 密码本
decrypt_msg = encrypt_msg.translate(password_table2) # 解密后的内容
print(decrypt_msg) # hello world

统计字符个数示例:

while True:
    msg = input(">:").strip() # my name is zhangsan,age is 25.
    if not msg: continue # if len(msg) == 0
    str_count = 0
    int_count = 0
    space_count = 0
    special_count = 0
    for i in msg:
        if i.isalpha():
            str_count += 1
        elif i.isdigit():
            int_count += 1
        elif i.isspace():
            space_count += 1
        else:
            special_count += 1
    print(f"str_count:{str_count},int_count:{int_count},space_count:{space_count},special_count:{special_count}") # str_count:21,int_count:2,space_count:5,special_count:2

布尔型bool

布尔类型很简单,就两个值 ,⼀个True(真),⼀个False(假), 它的作⽤主要是⽤作逻辑判断.

score = 540
good_score = 600

if score >= good_score:
    print("You passed the exam!")
else:
    print("You failed the exam.")

列表类型

list = []
listType = type(list)
# print(listType) #<class 'list'>

a_list = ['zhangsan','lisi','lili','a','b']
b_list = ['1','2']

# 索引
a_list[0] #找第0个位置的值(取第一个元素): zhangsan
a_list[-1] #找最后一个值: b
a_list[:] #取所有列表元素: ['zhangsan', 'lisi', 'lili', 'a', 'b']
a_list[:len(a_list)] #取所有列表元素: ['zhangsan', 'lisi', 'lili', 'a', 'b']
a_list[0:len(a_list)] #取所有列表元素: ['zhangsan', 'lisi', 'lili', 'a', 'b']
#a_list[0:n] #从0号取到n-1号元素
a_list[0:2] #取0号到1号元素: ['zhangsan', 'lisi']
a_list.index('b')  #反向索引找到b的位置: 4

# list的组合及复制
c_list = a_list + b_list #两个list组合成一个: ['zhangsan', 'lisi', 'lili', 'a', 'b', '1', '2']
d_list = a_list * 2 # a_list每个元素重复2次变新的list: ['zhangsan', 'lisi', 'lili', 'a', 'b', 'zhangsan', 'lisi', 'lili', 'a', 'b']

#是否存在
'b' in a_list # True b在a_list里面

#新增元素
a_list.append('c') #最后面新增一个元素
a_list.extend(['d','e']) # 一次新增多个元素
a_list.insert(0,'aa') # 在索引为0的元素前面新增元素

#删除元素
del a_list[0] #删除位置在0的值
a_list.pop() #删除最后一个元素
a_list.pop(2) #删除索引为2的值
a_list.remove('b') #删除第一个b的值


##列表删除所有指定元素
while 'lili' in a_list: 
	a_list.remove('lili') #删除所有lili元素
	
#循环生成list
e_list = [x+'a' for x in a_list] #循环生成新list: ['zhangsana', 'lisia', 'aa', 'ca', 'da']

#循环访问每个元素
for a in a_list:
	print('当前元素:',a)
	
#常用在list的函数
len(a_list) #长度
max(a_list) #取大
min(a_list) #取小

#其他常用的list函数
a_list.sort() #排序
a_list.reverse() #倒序,反转,原列表修改
a_list[::-1] #倒序,反转,生成一个新的列表
print(a_list.count('a')) #统计某个元素出现的次数

深浅copy

import copy

a = [1,2,3,['a','b'],4,5]
b = a.copy() # 浅copy
b[0] = 'd'
print(a[0],b[0]) # 1 d
b[3][0] = 'aa'
print(a[3][0],b[3][0]) # aa aa

c = copy.deepcopy(a) # 深copy
c[3][0] = 'cc' 
print(a[3][0],c[3][0]) # aa cc

列表生成式

# 生成一个list列表,如:['staff_0','staff_1',....]

# 方式一
staff_list = []
for i in range(30):
  staff_list.append(f"staff_{i}")
print(staff_list)

# 方式二:
staff_list2 = [f"staff_{i}" for i in range(30)]
print(staff_list2)

练习

列表去重(不能用set)

li = [1,2,5,10,2,6,34,56,2,17,89,5,34,10]
duplicate_num = [] # 存放需要去重的数,以及重复的次数

for i in li:
  i_show_count = li.count(i) # 每个值重复的次数
  if i_show_count > 1 and [i,i_show_count] not in duplicate_num:
    duplicate_num.append([i,i_show_count])

for item in duplicate_num:
  # n = item[0] # 重复的值
  # times = item[1] #重复值的次数
  n,times = item
  for j in range(times - 1):
    li.remove(n)
    print(f"删除了一次的值:{n}")

print(li)

打印结果:

除了一次的值:2
删除了一次的值:2
删除了一次的值:5
删除了一次的值:10
删除了一次的值:34
[1, 6, 56, 2, 17, 89, 5, 34, 10]

找到列表中第二大的数(不能用sort)

li = [1,2,5,10,2,6,34,56,2,17,89,5,34,10]
# 冒泡排序实现
for n in range(2): # 冒泡一次最后一位得到最大值,冒泡两次,倒数第二位得到第二最大值....以此类推
  for index in range(len(li) - 1):
    # 让当前索引值index,跟index+1比较
    item = li[index]
    if item > li[index + 1]: # index索引的值> index+1索引的值,进行交换
      li[index] = li[index + 1]
      li[index + 1] = item
  
print(li)
print(li[len(li)-2]) # 56

判断一个列表是不是另外一个列表的子列表

li = [1,2,5,10,2,6,34,56,2,17,89,5,34,10]
li2 = [1,5,10,11]

is_sub_list = True
for i in li2:
    if i not in li:
        is_sub_list = False
        print('li2不是li的子列表')
        break

if is_sub_list:
    print("li2是li的子列表")

找到离最大&最小的平均值最近的值

li = [1,2,5,10,2,6,33,56,2,17,89,5,34,10]

max_n = li[0]
min_n = li[1]

for i in li:
    if i > max_n:
        max_n = i
    if i < min_n:
        min_n = i
avg_n = (max_n + min_n) / 2
print(max_n,min_n,avg_n)

closest_n = li[0]
for n in li:
    if abs(avg_n - n) < abs(avg_n - closest_n):
        closest_n = n
        print(f'找到了更近的数:{closest_n}')

print(f'最终的答案:{closest_n}')

双色球福利彩票选购程序

双色球福利彩票:1个蓝球和6个红球,蓝球1-16数字,红球1-33数字。

red_ball = []
blue_ball = []

red_count = 0
while red_count < 6:
    input_number = input(f"请输入第{red_count + 1}个红球:")
    if not input_number.isdigit():
        print("输入的不是数字,请重新输入")
        continue
    input_number = int(input_number)
    if 0 < input_number <= 33 and input_number not in red_ball:
        red_ball.append(input_number)
        red_count += 1

blue_count = 0
while blue_count < 1:
    input_number = input(f"请输入第{blue_count + 1}个蓝球:")
    if not input_number.isdigit():
        print("输入的不是数字,请重新输入")
        continue
    input_number = int(input_number)
    if 0 < input_number <= 16 and input_number not in blue_ball:
        blue_ball.append(input_number)
        blue_count += 1

print(f"您选择的蓝球是:{blue_ball},红球是:{red_ball}")

上面代码进行优化,优化后的代码:

red_ball = []
blue_ball = []

li = [[6,33,'红球',red_ball],[1,16,'蓝球',blue_ball]]

for item in li:
    print(f"开始选择{item[2]}".center(50,'-'))
    count = 0
    while count < item[0]:
        input_number = input(f"请输入第{count + 1}个{item[2]}:").strip()
        if not input_number.isdigit():
            print("输入的不是数字,请重新输入")
            continue
        input_number = int(input_number)
        if 0 < input_number <= item[1] and input_number not in item[3]:
            item[3].append(input_number)
            count += 1

print(f"您选择的蓝球是:{blue_ball},红球是:{red_ball}")

常用运算符

其他省略

逻辑运算

  • and
  • or
  • not:取反(示例:not a > b)

成员运算

只有in, not in 2种, ⽤来测试数据中是否包含了⼀系列的成员

  • in:如果在指定的序列中找到值返回True,否则返回False。
  • not in:如果在指定的序列中没有找到值返回True,否则返回False。
>>> names
[ 'Jack', 'zhangsan', 'lisi', 'wangmazi', '⼩强']
>>> "Jack" in names
True
>>> "Jack" not in names
False
>>> s = "我是沙河最帅的仔"
>>> "沙河" in s
True

注意,可以⽤来测试字符串、列表、元组、字典、集合,但是不能测试数字类型

读取用户指令

name = input("What is your name? ").strip()
age = int(input("How old are you? "))
hobbie = input("What is your favorite hobby? ")
job = input("What is your job? ")

msg = f'''
---------- {name} ------------
name: {name}
age: {age},wow still young! in {40-age} years you will be 40
hobbie: {hobbie}
job: {job}
-------------  end ---------
'''
print(msg)

最终打印结果:

What is your name? mr zhang
How old are you? 28
What is your favorite hobby? money
What is your job? coder

---------- mr zhang ------------
name: mr zhang
age: 28,wow still young! in 12 years you will be 40
hobbie: money
job: coder
------------- end ---------

上面代码中strip方法就是把输入的前后的空格也去掉。

int方法就是把输入的字符串转换为数字类型。

循环

for循环

# 打印100 以内的偶数
for i in range(101):
    if i % 2 == 0:
        print(i)

# 打印50-100以内的偶数
for i in range(50,101):
    if i % 2 == 0:
        print(i)

# 打印100-50以内的偶数
for i in range(100,50,-1):
    if i % 2 == 0:
        print(i)

嵌套循环示例:

# 嵌套循环,打印1-6层的楼,每层楼10个房间
for floor in range(1,7):
    print(f"当前在{floor}层".center(50,'-'))
    for room in range(1,10):
        print(f"当前房间是{floor}0{room}室.")

控制台打印效果如下所示:

----------------------当前在1层-----------------------
当前房间是101室.
当前房间是102室.
当前房间是103室.
当前房间是104室.
当前房间是105室.
当前房间是106室.
当前房间是107室.
当前房间是108室.
当前房间是109室.
----------------------当前在2层-----------------------
当前房间是201室.
当前房间是202室.
当前房间是203室.
.........

跳出循环:

# break、continue的使用
# 第三层的时候跳出循环,执行下面的循环
# 第四层第四间房间的时候,跳出当前循环
for floor in range(1,7):
    print(f"当前在{floor}层".center(50,'-'))
    if floor == 3:
        print('第三层跳出...')
        continue
    for room in range(1,10):
        if floor == 4 and room == 4:
            print('第四层第四个房间跳出...')
            break
        print(f"当前房间是{floor}0{room}室.")

注意:上面代码中break只是跳出了第二个for循环,下面第五层会接着循环,那如何跳出这两个for循环呢,示例代码:

# 跳出多层循环
should_break  = False
for floor in range(1,7):
    print(f"当前在{floor}层".center(50,'-'))
    if floor == 3:
        print('第三层跳出...')
        continue
    for room in range(1,10):
        if floor == 4 and room == 4:
            print('第四层第四个房间跳出...')
            should_break = True
            break
        print(f"当前房间是{floor}0{room}室.")
    if should_break:
        break

打印9*9乘法表:

# 9*9乘法表
for i in range(1,10):
  for j in range(1,i + 1):
    print (f"{i}X{j} = {i * j}",end= ' ' if j < i else '\n')

方式二:主要是换行的方式:

for i in range(1,10):
  for j in range(1,i + 1):
    print (f"{i}X{j} = {i * j}",end= " ")
  print() #  换行 默认\n

最终结果:

1X1 = 1
2X1 = 2 2X2 = 4
3X1 = 3 3X2 = 6 3X3 = 9
4X1 = 4 4X2 = 8 4X3 = 12 4X4 = 16
5X1 = 5 5X2 = 10 5X3 = 15 5X4 = 20 5X5 = 25
6X1 = 6 6X2 = 12 6X3 = 18 6X4 = 24 6X5 = 30 6X6 = 36
7X1 = 7 7X2 = 14 7X3 = 21 7X4 = 28 7X5 = 35 7X6 = 42 7X7 = 49
8X1 = 8 8X2 = 16 8X3 = 24 8X4 = 32 8X5 = 40 8X6 = 48 8X7 = 56 8X8 = 64
9X1 = 9 9X2 = 18 9X3 = 27 9X4 = 36 9X5 = 45 9X6 = 54 9X7 = 63 9X8 = 72 9X9 = 81

求出100以内的所有素数

素数(Prime Number) 是指在一个大于1的自然数中,除了1和此整数自身外,不能被其他自然数整除的数。换句话说,只有两个正因数(1和自己)的自然数即为素数。最小的素数是2,它也是唯一的偶数素数。

例如,数字5有因数1和5;数字7有因数1和7;数字11有因数1和11……这些数字都是素数。而数字4除了因数1和4外,还有因数2,所以它不是素数。

# 求出100以内的所有素数
prime_num = 0 # 累积100内的素数个数
for i in range(2,101):
  is_prime_number = True
  for j in range(2,i):
    if i % j == 0:
      is_prime_number = False
      break
  if is_prime_number:
    prime_num += 1
    print(f"{i}是素数,累计的素数个数:{prime_num}")

上面代码我们可以使用for...else进行优化:

prime_num = 0 # 累积100内的素数个数
for i in range(2,101):
  for j in range(2,i):
    if i % j == 0:
      break
  else:
    prime_num += 1
    print(f"{i}是素数,累计的素数个数:{prime_num}")

打印3角形:

# 打印3角形
for i in range(11):
    if i <= 5:
        print("* " * i)
    else:
        print("* " * (11 - i))

展示效果:

*
* *
* * *
* * * *
* * * * *
* * * * *
* * * *
* * *
* *
*

while 循环

# while 循环
count = 0
while count < 10:
    count += 1
    print('loop:',count)

用while实现循环猜年龄,允许用户猜3次,若还不对,告诉他,你真笨,还想继续猜么?如果用户选择yes,就让他继续,如果选择no,就退出。

age = 20
count = 0

while count < 3:
  guess = input('请猜下年龄>:')
  if guess.isdigit():
      guess = int(guess)
  else:
     print('不识别的指令,请重新输入...')
     continue
  if guess > age:
      print('猜大了...')
  elif guess < age:
      print('猜小了...')
  else:
      print('猜对了...')
      break
  count += 1
  if count == 3:
      cmd = input('你笨,你猜了3次,还想继续猜么?(y/n)').strip()
      if cmd in ['y','Y','yes','Yes']:
          count = 0
      else:
          print('退出游戏...')

for...else/while...else

for...else:

for i in range(10):
  print(i)
  if i == 5:
    break
else: #当循环正常结束时(没有被break,exit...),则执行
  print('循环结束')

print('done....')

while...else:

count = 0
while count < 10:
  print(count)
  count += 1
  if count == 5:
    break
else: #当循环正常结束时(没有被break,exit...),则执行
  print('循环结束')

练习

存款多少年才能翻倍?

1万本金,利息是0.0325每年,问年本带息多少年能翻倍?

money = 10000
year = 0
while money <= 20000:
  money = money * (1 + 0.0325)
  year += 1
  print(f"第{year}年,本金是{money}元")

结果如下所示:

第1年,本金是10325.0元
第2年,本金是10660.5625元
第3年,本金是11007.03078125元
第4年,本金是11364.759281640625元
第5年,本金是11734.113958293945元
第6年,本金是12115.472661938498元
第7年,本金是12509.2255234515元
第8年,本金是12915.775352963674元
第9年,本金是13335.538051934993元
第10年,本金是13768.94303862288元
第11年,本金是14216.433687378123元
第12年,本金是14678.467782217911元
第13年,本金是15155.517985139993元
第14年,本金是15648.072319657043元
第15年,本金是16156.634670045896元
第16年,本金是16681.725296822387元
第17年,本金是17223.881368969116元
第18年,本金是17783.65751346061元
第19年,本金是18361.62638264808元
第20年,本金是18958.37924008414元
第21年,本金是19574.526565386874元
第22年,本金是20210.698678761946元

小球坠落反弹经过多少米

一个小球,从100米高空坠落,每次反弹回原来一半高度,问第10次反弹完,小球经过多少米?

height = 100
distance = 0
for i in range(10):
  distance += height + height /2 
  height = height / 2
  print(f"{i + 1}次反弹玩,小球经过的高度为{distance}")

结果如下:

1次反弹玩,小球经过的高度为150.0
2次反弹玩,小球经过的高度为225.0
3次反弹玩,小球经过的高度为262.5
4次反弹玩,小球经过的高度为281.25
5次反弹玩,小球经过的高度为290.625
6次反弹玩,小球经过的高度为295.3125
7次反弹玩,小球经过的高度为297.65625
8次反弹玩,小球经过的高度为298.828125
9次反弹玩,小球经过的高度为299.4140625
10次反弹玩,小球经过的高度为299.70703125

小猴子吃桃

有一堆桃子,猴子每天吃桃子总数的一半并多吃一个。吃了10天,到第11天只剩下一个桃子。问,猴子吃之前,一共是多少个桃子。

n = 1
day = 11
while day > 1:
   n = (n + 1) * 2 # 上一天吃之前的桃子数量
   day -= 1
   print(f"第{day}天,没有吃之前桃子数量:{n}")

结果:

第10天,没有吃之前桃子数量:4
第9天,没有吃之前桃子数量:10
第8天,没有吃之前桃子数量:22
第7天,没有吃之前桃子数量:46
第6天,没有吃之前桃子数量:94
第5天,没有吃之前桃子数量:190
第4天,没有吃之前桃子数量:382
第3天,没有吃之前桃子数量:766
第2天,没有吃之前桃子数量:1534
第1天,没有吃之前桃子数量:3070

寻找列表中的最大值和最小值

data = [2,6,23,22,19,50,29,8,14,1]
max_data = data[0]
min_data = data[0]
for i in data:
  if i > max_data:
      max_data = i
  if i < min_data:
      min_data = i

print(max_data,min_data)

求2组列表的数据组合

从两个列表中各取1个数,如果这两个数的和等于10,则以列表的方式输出这两个数。

data1 = [4,8,9,20,5,12,22]
data2 = [9,12,6,1,5,14,7]

for i in data1:
  for j in data2:
    if i + j == 10:
      print([i,j])

结果:

[4, 6]
[9, 1]
[5, 5]

数据类型-元组

元组是只读列表,一旦创建,不可修改。只可进行查询、切片、循环操作。

# 元组定义
names = ('zhangsan','lisi','tom')

index = names.index('lisi')
# print(f"索引为{index}的元素为{names[index]}")

# 切片
r1 = names[0:3]
# print(r1) # ('zhangsan', 'lisi', 'tom')

r2 = names[0::2]
# print(r2) # ('zhangsan', 'tom')  步长为2

# 不能修改
# names[0] = 'zhangsan1' # TypeError: 'tuple' object does not support item assignment

注意点,如果在括号内只加一个数字,没有逗号,那么它的类型就是数字类型。示例代码如下:

n = (10)
print(type(n)) # <class 'int'>

m = (10,)
print(type(m)) # <class 'tuple'>

数据类型-字典dict

dict定义

{key1:value1, key2:value2}

:号左边是key, 右边是value

dict特性

  • 1.key-value结构
  • 2.key必须为不可变数据类型(字符串、数字、元组), (hashtable)
  • 3.key 必须唯⼀ ,(hashtable)
  • 4.⼀个key对应的value可存放任意数据类型,可修改、可以不唯⼀
  • 5.可嵌套,即value也可是dict
  • 6.py3.7之前是⽆序的, 3.7开始变成有序的了, ordered_dict
  • 7.查询速度快,且不受dict的⼤⼩影响。

dict⽤法

# 创建:方法一
info = {
  "name": 'zhangsan',
  "age": 18
}
# print(info) # {'name': 'zhangsan', 'age': 18}

# 创建:方法二
info2 = dict(name='zhangsan', age=18)
# print(info2) # {'name': 'zhangsan', 'age': 18}

# 新增
info3 = {
  "name": 'zhangsan',
  "age": 18
}
info3['sex'] = 'male'
# print(info3) # {'name': 'zhangsan', 'age': 18, 'sex': 'male'}

# 检查式新增
info4 = {
   "name": 'zhangsan',
  "age": 18
}
info4.setdefault('sex', 'male') 
# print(info4) # {'name': 'zhangsan', 'age': 18, 'sex': 'male'}
res = info4.setdefault('name','lisi')
# print(res) # zhangsan 如果原来存在当前新增的属性,那么会直接返回原来属性的值

# 修改
info5 = {
  "name": 'zhangsan',
  "age": 18
}
info5['name'] = 'lisi'
# print(info5) # {'name': 'lisi', 'age': 18}

# 合并修改
info6 = {
  "name": 'zhangsan',
  "age": 18
}
info6_1 = {
  "name": 'lisi',
  "sex": 'male'
}
info6.update(info6_1)
# print(info6) # {'name': 'lisi', 'age': 18, 'sex': 'male'}

# 查
info7 = {
  "name": 'zhangsan',
  "age": 18
}

re7 = info7.get('name')
# print(re7) # zhangsan
re8 = info7.get('sex') # 如果取不到,就返回None
# print(re8) # None

# re9 = info7['sex'] # 这种方式取值如果取不到,会报错
# print(re9) # KeyError: 'sex'

re10 = "name" in info7 # 判断是否包含某个属性
# print(re10) # True

re10_1 = info7.keys() # 返回⼀个包含字典所有KEY的列表;
# print(re10_1) # dict_keys(['name', 'age'])
re10_2 = info7.values() # 返回⼀个包含字典所有value的列表;
# print(re10_2) # dict_values(['zhangsan', 18])
re10_3 = info7.items() # 返回⼀个包含所有(键,值)元组的列表;
# print(re10_3) # dict_items([('name', 'zhangsan'), ('age', 18)])


# 删
info8 = {
  "name": 'zhangsan',
  "age": 18,
  "sex": 'male',
  "address": 'beijing'
}
info8.pop('name') # 删除存在的属性,并返回删除属性的值
# print(info8) # {'age': 18}

del info8['age'] # 删除指定key,同pop⽅法
# print(info8) # {'sex': 'male'}

info8.popitem() #  以LIFO的⽅式删除⼀对值
# print(info8) # {'sex': 'male'}
info8.clear() # 清空字典
# print(info8) # {}

# 循环
info9 = {
  "name": 'zhangsan',
  "age": 18,
  "sex": 'male',
  "address": 'beijing'
}

# for k in info9.keys(): # 遍历字典的key
#   print(k)

# for k,v in info9.items(): # 遍历字典的key和value
#   print(k,v)

# for k in info9: # 遍历字典的key,推荐⽤这种,效率速度最快
#   print(k,info9[k])


# 特殊⽅法-fromkeys : 批量⽣成多个k,v的dict
names = ['zhangsan','lisi','wangwu']
info10 = dict.fromkeys(names,'test') # 如果不设置默认值,默认是None
# print(info10) # {'zhangsan': 'test', 'lisi': 'test', 'wangwu': 'test'}

# 特殊⽅法-Copy: 浅copy , 同列表的copy⼀样
info11 = {
  "name": 'zhangsan',
  "age": 18,
  "sex": 'male',
  "address": 'beijing'
}
info12 = info11.copy()
# print(info12)

# 求长度-len 方法
info13 = {
  "name": 'zhangsan',
  "age": 18,
  "sex": 'male',
  "address": 'beijing'
}
print(len(info13)) # 4

dict 为何查询速度快

因为dict是基于hashtable实现的, hashtable的原理导致你查询速度就是O(1),意思就是你即使有1亿个数据,查询某个值也只需1次搞定。

先⽤最简单的例⼦理解,看以下列表nums,让你⽤最快最⾼效的⽅式找出100这个数,你怎么找?

>>> nums
[125, 247, 53, 260, 33, 6, 132, 198, 151, 110, 95, 163, 116, 295, 289, 200, 142, 153,
126, 41, 294, 290, 31, 34, 241, 132, 191, 82, 246, 123, 19, 171, 37, 130, 139, 250,
53, 241, 84, 172, 14, 120, 138, 92, 294, 116, 269, 104, 278, 146, 12, 293, 135, 240,
289, 121, 295, 50, 60, 198, 10, 57, 248, 79, 169, 298, 292, 64, 298, 3, 9, 152, 268,
178, 0, 136, 165, 53, 146, 78, 270, 104, 116, 283, 177, 145, 142, 71, 176, 33, 235,
134, 188, 29, 39, 15, 136, 137, 16, 189]
  • 1.先排序
  • 2.再折半(2分)算法查找

粗识hashtable

hashtable(散列表) ⽤了个更nb的⽅式 , 直接把你的key通过hash函数算出来个数字,然后扔在hashtable的某个位置。

image

你再去通过dict.get("alex")获取的时候,它只需要把 alex再变成⼀个hash值,去找在这个表⾥的值 就可以了。这样, ⽆论你的dict多⼤, 你查询速度都不会受影响。

练习

数值分类

把下⾯列表中的值进⾏分类 ,变成dict。

Input : test_list = [4, 6, 6, 4, 2, 2, 4, 8, 5, 8]
Output : {4: [4, 4, 4], 6: [6, 6], 2: [2, 2], 8: [8, 8], 5: [5]}
需求 : 值⼀样的要分类存在⼀个key⾥

代码实现:

test_list = [4, 6, 6, 4, 2, 2, 4, 8, 5, 8]
obj = {}
for i in test_list:
    if i not in obj:
        obj[i] = [i]
    else:
        obj[i].append(i)

print(obj) # {4: [4, 4, 4], 6: [6, 6], 2: [2, 2], 8: [8, 8], 5: [5]}

同字⺟异序词

把所有下表中同字⺟异序词找出来:

arr = ['cat', 'dog', 'tac', 'god', 'act']

结果:[ ['cat','tac','act'],['god','dog']]

代码实现:

arr = ['cat', 'dog', 'tac', 'god', 'act']

dic = {
    
}

for i in arr:
    token_key = list(i)
    token_key.sort()
    token_key = tuple(token_key) # 把列表转换成元组当字典的key
    if token_key not in dic:
        dic[token_key] = [i]
    else:
        dic[token_key].append(i)

result = dic.values() # dict_values([['cat', 'tac', 'act'], ['dog', 'god']])
result = list(result) # [['cat', 'tac', 'act'], ['dog', 'god']] 相对于上面去掉了dict_values
# print(result)

数据类型-集合set

  • ⾥⾯的元素不可变,代表你不能存⼀个list、dict 在集合⾥,字符串、数字、元组等不可变类型可以存
  • 天⽣去重,在集合⾥没办法存重复的元素
  • ⽆序,不像列表⼀样通过索引来标记在列表中的位置 ,元素是⽆序的,集合中的元素没有先后之分,如集
    合{3,4,5}和{3,5,4}算作同⼀个集合
  • 关系测试(作⽤)

基于上⾯的特性,我们可以⽤集合来⼲2件事,去重和关系运算

image

增删查

# 去重
a = {1,2,3,4,5,2,3,5}
a = set(a)
a = list(a)
# print(a) # [1, 2, 3, 4, 5]

# 增
a1 = {1,2,3,4,5}
a1.add(8)
# print(a1) # {1, 2, 3, 4, 5, 8}

# 查
a2 = {1,2,3,4,5}
# print(1 in a2) # True

# 删除
a3 = {1,2,3,4,5}
a3.remove(1) # {2, 3, 4, 5} 若不存在,会报错
a3.discard(1) # {3, 4, 5}  删除指定的值 ,若该值不存在,也不会报错
a3.pop() # 随机删除一个值,并返回该值
a3.clear() # 清空所有
a3.copy() # 复制

注:不能修改,⼀旦创建,不可修改⾥⾯的值

关系测试

# 去重
a = {1,2,3,4,5,2,3,5}
a = set(a)
a = list(a)
# print(a) # [1, 2, 3, 4, 5]

# 增
a1 = {1,2,3,4,5}
a1.add(8)
# print(a1) # {1, 2, 3, 4, 5, 8}

# 查
a2 = {1,2,3,4,5}
# print(1 in a2) # True

# 删除
a3 = {1,2,3,4,5}
a3.remove(1) # {2, 3, 4, 5} 若不存在,会报错
a3.discard(1) # {3, 4, 5}  删除指定的值 ,若该值不存在,也不会报错
a3.pop() # 随机删除一个值,并返回该值
a3.clear() # 清空所有
a3.copy() # 复制


# 交集
names1 = {'zhangsan','lisi','tom'}
names2 = {'zhangsan','lisi','jack'}
rs1 = names1 & names2 
# print(rs1) #  {'zhangsan', 'lisi'}
rs2 = names1.intersection(names2)
# print(rs2) #  {'zhangsan', 'lisi'}

# 并集
names_u1 = {'zhangsan','lisi','tom'}
names_u2 = {'zhangsan','lisi','jack'}
rs_u1 = names_u1 | names_u2 
rs_u2 = names_u1.union(names_u2) 
# print(rs_u1) # {'jack', 'tom', 'zhangsan', 'lisi'}
# print(rs_u2) # {'jack', 'tom', 'zhangsan', 'lisi'}

# 差集
names_d1 = {'zhangsan','lisi','tom'}
names_d2 = {'zhangsan','lisi','jack'}
rs_d1 = names_d1 - names_d2 
rs_d2 = names_d1.difference(names_d2)
# print(rs_d1) # {'tom'}
# print(rs_d2) # {'tom'}
rs_d3 = names_d2 - names_d1
rs_d4 = names_d2.difference(names_d1)
# print(rs_d3) # {'jack'}
# print(rs_d4) # {'jack'}

# 对称差集
names_sd1 = {'zhangsan','lisi','tom'}
names_sd2 = {'zhangsan','lisi','jack'}
rs_sd1 = names_sd1 ^ names_sd2
rs_sd2 = names_sd1.symmetric_difference(names_sd2)
# print(rs_sd1) # {'tom', 'jack'}
# print(rs_sd2) # {'tom', 'jack'}

# ⼦集、⽗集
names_s1 = {'zhangsan','lisi','tom'}
names_s2 = {'zhangsan','lisi','jack'}
names_s3 = {'zhangsan','lisi','jack','tom'}
# print(names_s1.issubset(names_s3)) # True  names_s1 是不是 names_s3的子集
# print(names_s1.issuperset(names_s3)) # False names_s1是不是 names_s3的⽗集
# print(names_s3.issuperset(names_s1)) # True names_s3 是不是 names_s1的⽗集

# 测试后修改
test_names1 = {'zhangsan','lisi','tom'}
test_names2 = {'zhangsan','lisi','jack'}
test_names1.difference_update(test_names2) # 把差集的结果赋值给test_names1
print(test_names1) # {'tom'}

二进制

⼆进制是计算技术中⼴泛采⽤的⼀种数制。⼆进制数据是⽤0和1两个数码来表示的数。它的基数为2,进位规则是“逢⼆进⼀”,由18世纪德国数理哲学⼤师莱布尼兹发现。当前的计算机系统使⽤的都是⼆进制系统,数据在计算机中主要是以补码的形式存储的。计算机中的⼆进制则是⼀个⾮常微⼩的开关,⽤“开”来表示1,“关”来表示0

⼆进制与⼗进制转换

⼆进制的第n位代表的⼗进制值都刚好遵循着2的n次⽅这个规律

填位⼤法:
先把他们代表的值依次写出来,然后再根据10进制的值把数填到相应位置,就好了。
⼗进制转⼆进制⽅法相同,只要对照⼆进制为1的那⼀位对应的⼗进制值相加就可以了。

image

⼆进制位运算

程序中的所有数在内存中都是以⼆进制的形式储存的。位运算就是直接对整数在内存中的⼆进制位进⾏操作。

下表中变量 a 为 60,b 为 13,⼆进制格式如下:

a = 0011 1100
b = 0000 1101
  • &:按位与运算符:参与运算的两个值,如果两个相应位都为1,则该位的结果为1,否则为0(快记法:与运算, 位相乘)
(a & b) 输出结果 12 ,⼆进制解释: 00001100
  • |:按位或运算符:只要对应的⼆个⼆进位有⼀个为1时,结果位就为1。(快记:或运算, 位相加)
(a | b) 输出结果 61 ,⼆进制解释: 00111101
  • ^:按位异或运算符:当两对应的⼆进位相异时,结果为1(快记:位相减)
(a ^ b) 输出结果 49 ,⼆进制解释: 00110001
  • ~:按位取反运算符:对数据的每个⼆进制位取反,即把1变为0,把0变为1 。(快记:~x=-(x+1)
(~a ) 输出结果 -61 ,⼆进制解释: 11000011,在⼀个有符号⼆进制数的补码形式。
  • <<:左移动运算符:运算数的各⼆进位全部左移若⼲位,由 <<右边的数字指定了移动的位数,⾼位丢弃,低位补0。
a << 2 输出结果 240 ,⼆进制解释: 11110000
  • :右移动运算符:把">>"左边的运算数的各⼆进位全部右移若⼲位,>> 右边的数字指定了移动的位数

a >> 2 输出结果 15 ,⼆进制解释: 00001111

按位取反深⼊:https://blog.csdn.net/Coder__CS/article/details/79186677?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_baidulandingword-8&spm=1001.2101.3001.4242

初识编码

ASCII编码

计算机只认识⼆进制,⽣活中的数字要想让计算机理解就必须转换成⼆进制。⼗进制到⼆进制的转换只能解决计算机理解数字的问题,那么⽂字要怎么让计算机理解呢?

于是我们就选择了⼀种曲线救国的⽅式,既然数字可以转换成⼗进制,我们只要想办法把⽂字转换成数字,这样⽂字不就可以表示成⼆进制了么?

image

可是⽂字应该怎么转换成数字呢?我们可以拿着⼀个数字来对⽐对应表找到相应的⽂字

image

ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字⺟的⼀套电脑编码系统,主要⽤于显示现代英语和其他⻄欧语⾔。它是现今最通⽤的单字节编码系统,并等同于国际标准ISO/IEC 646。

由于计算机是美国⼈发明的,因此,最早只有127个字⺟被编码到计算机⾥,也就是⼤⼩写英⽂字⺟、数字和⼀些符号,这个编码表被称为 ASCII 编码,⽐如⼤写字⺟ A 的编码是 65 ,⼩写字⺟ z 的编码是 122 。后128个称为扩展ASCII码,主要是⼀些特殊字符。

Ord('a')
Chr(90)

如下示例:

⼀个空格对应的数字是0 翻译成⼆进制就是0(注意字符'0'和整数0是不同的)
⼀个对勾√对应的数字是251 翻译成⼆进制就是11111011

提问:假如我们要打印两个空格⼀个对勾 写作⼆进制就应该是 0011111011, 但是问题来了,我们怎么知道从哪⼉到哪⼉是⼀个字符呢?

既然⼀共就这255个字符,那最⻓的也不过是11111111⼋位,不如我们就把所有的⼆进制都转换成8位的,不⾜的⽤0来替换。

这样⼀来,刚刚的两个空格⼀个对勾就写作000000000000000011111011,读取的时候只要每次读8个字符就能知道每个字符的⼆进制值啦。

在这⾥,每⼀位0或者1所占的空间单位为bit(⽐特),这是计算机中最⼩的表示单位

每8个bit组成⼀个字节,这是计算机中最⼩的存储单位(毕竟你是没有办法存储半个字符的)

bit 位,计算机中最⼩的表示单位
8bit = 1bytes 字节,最⼩的存储单位,1bytes缩写为1B
1KB=1024B
1MB=1024KB
1GB=1024MB
1TB=1024GB
1PB=1024TB
1EB=1024PB
1ZB=1024EB
1YB=1024ZB
1BB=1024YB

计算机如何识别中文?

我们设计出了GB2312编码表,⻓成下⾯的样⼦。⼀共存了6763个汉字。
image

这个表格⽐较⼤,像上⾯的⼀块块的⽂字区域有72个,这导致通过⼀个字节是没办法表示⼀个汉字的(因为⼀个字节最多允许256个字符变种,你现在6千多个,只能2个字节啦,2**16=65535个变种)。

如何区别连在⼀起的2个字节是代表2个英⽂字⺟,还是⼀个中⽂汉字呢? 中国⼈如此聪明,决定,如果2个字节连在⼀起,且每个字节的第1位(也就是相当于128的那个2进制位)如果是1,就代表这是个中⽂,这个⾸位是128的字节被称为⾼字节。也就是2个⾼字节连在⼀起,必然就是⼀个中⽂。你怎么如此笃定?因为0-127已经表示了英⽂的绝⼤部分字符,128-255是ASCII的扩展表,表示的都是极特殊的字符,⼀般没什么⽤。所以中国⼈就直接拿来⽤了

⾃1980年发布gb2312之后,中⽂⼀直⽤着没啥问题,随着个⼈电脑进⼊千家万户,有⼈发现,⾃⼰的名字竟然打印不出来,因为起的太⽣僻了。

于是1995年, 砖家们⼜升级了gb2312, 加⼊更多字符,连什么藏语、维吾尔语、⽇语、韩语、蒙古语什么的统统都包含进去了,国家统⼀亚洲的ᰀ⼼从这些基础⼯作中就可⻅⼀斑哈。 这个编码叫GBK,⼀直到现在,我们的windows电脑中⽂版本的编码就是GBK.

Unicode编码

中国⼈在搞⾃⼰编码的同时,世界上其它⾮英语国家也得⽤电脑呀,于是都搞出了⾃⼰的编码,你可以想得到的是,全世界有上百种语⾔,⽇本把⽇⽂编到Shift_JIS⾥,韩国把韩⽂编到Euc-kr⾥。

ISO(国际标谁化组织)的国际组织决定着⼿解决这个问题。他们采⽤的⽅法很简单:废了所有的地区性编码⽅案,᯿新搞⼀个包括了地球上所有⽂化、所有字⺟和符号 的编码!他们打算叫它”Universal Multiple-Octet Coded Character Set”,简称 UCS, 俗称 “unicode“。

Unicode把所有语⾔都统⼀到⼀套编码⾥,这样就不会再有乱码问题了。Unicode 2-4字节 已经收录136690个字符,并还在⼀直不断扩张中…

Unicode标准也在不断发展,但最常⽤的是⽤两个字节表示⼀个字符(如果要⽤到⾮常偏僻的字符,就需要4个字节)

Unicode有2个特点:

  • ⽀持全球所有语⾔
  • 包含跟各种语⾔的映射关系,所以可以跟各种语⾔的编码⾃由转换,也就是说,即使你gbk编码的⽂字 ,想转成unicode很容易。

为何unicode可以跟其它语⾔互相转换呢? 因为有跟所有语⾔都有对应关系哈,这样做的好处是可以让那些已经⽤gbk或其它编码写好的软件容易的转成unicode编码 ,利于unicode的推⼴。 下图就是unicode跟中⽂编码的对应关系:

image

UTF-8

帝国(unicode)统⼀后,新的问题⼜出现了:如果统⼀成Unicode编码,乱码问题从此消失了。但是,如果你写的⽂本基本上全部是英⽂的话,⽤Unicode编码⽐ASCII编码需要多⼀倍的存储空间,由于计算机的内存⽐较⼤,并且字符串在内容中表示时也不会特别⼤,所以内容可以使⽤unicode来处理,但是存储和⽹络传输时⼀般数据都会⾮常多,且互联⽹刚出现时,⽹速⼜慢,这个数据占⽤空间⼤⼀倍将是⽆法容忍的。

为了解决存储和⽹络传输的问题,出现了Unicode Transformation Format,学术名UTF,即:对unicode字符进⾏转换,以便于在存储和⽹络传输时可以节省空间!

  • UTF-8: 使⽤1、2、3、4个字节表示所有字符;优先使⽤1个字符、⽆法满⾜则使增加⼀个字节,最多4个字节。英⽂占1个字节、欧洲语系占2个、东亚占3个,其它及特殊字符占4个
  • UTF-16: 使⽤2、4个字节表示所有字符;优先使⽤2个字节,否则使⽤4个字节表示。
  • UTF-32: 使⽤4个字节表示所有字符;

注意:UTF 是为unicode编码 设计 的⼀种 在存储 和传输时节省空间的编码⽅案,它是Unicode的⼀种实现⽅式.

如果你要传输的⽂本包含⼤ᰁ英⽂字符,⽤UTF-8编码就能节省空间:

image

从上⾯的表格还可以发现,UTF-8编码有⼀个额外的好处,就是ASCII编码实际上可以被看成是UTF-8编码的⼀部分,所以,⼤量只⽀持ASCII编码的历史遗留软件可以在UTF-8编码下继续⼯作。

搞清楚了ASCII、Unicode和UTF-8的关系,我们就可以总结⼀下现在计算机系统通⽤的字符编码⼯作⽅式:

image

unicode官⽹:https://home.unicode.org/
unicode 中⽂表: http://www.chi2ko.com/tool/CJK.htm

常⽤编码介绍⼀览表

image

py2 vs py3 编码区别

  • py2默认ASCII
  • py3默认unicode,文件编码utf-8

py2写中⽂的正确姿势,在代码⽂件第⼀⾏,做声明,告诉py解释器,后⾯的代码是⽤utf-8编码的,你⽤utf-8编码来解析它

# -*- encoding:utf-8 -*-
name = "张三"
print(name)

不过注意如果你的电脑 是windows系统 , 你的系统默认编码是GBK ,你声明的时候要声明成GBK, 不能是utf-8, 否则依然是乱码,因为gbk⾃然不认识utf-8。

到了Py3推出后,终于把默认编码改成了unicode, 同时⽂件存储编码变成了utf-8,意味着,不⽤任何声明,你就可以写各种语⾔⽂字在你的Python程序⾥。

⼗六进制

16进制,英⽂名称Hexadecimal(简写Hex), 在数学中是⼀种逢16进1的进位制。⼀般⽤数字0到9和字⺟A到F(或 a~f )表示,其中: A~F 表示10~15,这些称作⼗六进制数字,⽐如⼗进制13⽤16进制表示是D, 28⽤16进制是1C。

0 1 2 3 4 5 6 7 8 9  A  B  C  D  E  F
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

每个16进制,占4-bit, 所以要⽤16进制来表示⼀个字节的话,正好2个16进制数字就可以啦

⽐如拿234这个数来算:

image

展示起来,更简洁,应该是16进制在计算机⾥被⼴泛应⽤的主要原因.

16进制在计算机领域应⽤普遍,常⻅的有html\css的颜⾊表、mac地址、字符编码等都⽤16进制来表示。 这是因为将4个位元(Bit)化成单独的16进制数字不太困难。1字节可以表示成2个连续的16进制数字, 也可以表示8个2进制数。这种混合表示法就容易令⼈混淆,因此需要⼀些字⾸、字尾或下标来显示这个值到底是16进制还是2进制 ,所以在C语⾔、C++、Shell、Python、Java语⾔及其他相近的语⾔使⽤字⾸“0x”来标示16进制,例如“0x5A3”代表1443。

image

示例:

a = hex(234)
print(a) # 0xea

三元运算

⼜称三⽬运算,是对if...else...的⼀种简写

假设现在有两个数字,我们希望获得其中较⼤的⼀个,那么可以使⽤ if else 语句,例如:

if a>b: 
 max = a
else: 
 max = b

⽤三元运算:

max = a if a > b else b

上⾯语句的含义是:

  • 如果 a>b 成⽴,就把 a 作为整个表达式的值,并赋给变ᰁ max;
  • 如果 a> b 不成⽴,就把 b 作为整个表达式的值,并赋给变ᰁ max.

三元嵌套

Python 三元运算符⽀持嵌套,就可以构成更加复杂的表达式。在嵌套时需要注意 if 和 else 的配对,例如:

>>> a,b,c,d = 3,5,7,9
>>>
>>> a if a > b else c if c > d else d
9

应该理解为:

 a if a > b else (c if c > d else d)

函数

函数的定义

def sayhello(name):
    print('hello',name)

sayhello('张三')

函数的返回值

def mobile_check(phone_num):
    if len(phone_num) == 11:
        if phone_num.isdigit():
            if phone_num.startswith('1'): # 1开头
                return True

s = '13651054609'
if mobile_check(s): # 结果是True
    print("合法手机号...")

特点:

  • 默认返回值就None,
  • 可返回任意数据类型
  • 可返回多个值

函数返回多个值:

def stu_registriation_form():
    form = {
        "name": input("Name:").strip(),
        "age": input("Age:").strip(),
        "phone": input("Phone:").strip()
    }

    info_pass_flag = True  # 如果字段全填了,就是True
    for k, v in form.items():
        if len(v) == 0:  # 没写东西
            info_pass_flag = False
            break

    return form, info_pass_flag


stu_info, flag = stu_registriation_form() # 接收2个值 
print(stu_info)
print(flag)

函数传值类型

传值类型:可以把任意值做为参数传递给函数,这些值可整体分为2种,可变类型、不可类型,2种值传给函数,在函数里修改时,产生的效果不同

  • 不可变类型做参数:在函数内修改外部传进来的不可变类型时, 会在函数内部生成一个该参数的copy , 并不会影响原来函数外部的值
  • 可变类型做参数:可变类型,如列表、dict, 传到函数内部,其实只是传递了该列表\dict的整体内存地址,函数内部可直接修改函数外部的这个list or dict

示例:

def change_data(name,hobbies):
    name = "zhangsan"   # 修改只在函数内生效
    hobbies.append("wangwu")  # 在函数内往外部列表添加值 
    hobbies[1] = "dog"   # 修改列表元素
    print("in func:",name,hobbies)


my_name = "张三" # 不可变类型
my_hobbies = ["Money","Cat"] # 可变类型
change_data(my_name,my_hobbies)
print(my_name,my_hobbies) # 张三 ['Money', 'dog', 'wangwu']

从上面打印结果可以看出:列表被函数修改了,而外面的字符串并无变化

参数类型-位置参数和关键参数

必备参数(位置参数)

  • 必备,不传值会报错
  • 传的值是有顺序的,从左到右,每个参数一一对应

关键字参数

  • 赋值时指定参数名,不按位置顺序了
  • 如果和必备参数混用,必须放在位置参数后边
def stu_form(name, age, major, phone):
    info = f'''
    Name : {name},
    Age  : {age},
    Major: {major},
    Phone: {phone}
    '''
    print(info)
stu_form(major="Computer Science", name="zhangsan", phone=11000000000, age=22)

打印结果:


    Name : zhangsan,
    Age  : 22,
    Major: Computer Science,
    Phone: 11000000000

和位置参数混用

def stu_form(name, age, major, phone):
    info = f'''
    Name : {name},
    Age  : {age},
    Major: {major},
    Phone: {phone}
    '''
    print(info)
stu_form('zhangsan',22,phone=1100000000,major="Computer Science") # 和位置参数混用

参数类型-默认参数&不定长参数

默认参数

对于一些调用时非必选的参数,可设置成默认参数,这样,即使用户不填,也不影响函数正常运行.

def stu_form(name, age, major, phone, nationality='CN'):
    info = f'''
    Name : {name},
    Age  : {age},
    Major: {major},
    Phone: {phone},
    Nation: {nationality}
    '''
    print(info)


stu_form( "zhangsan",  major="IT", age=25, phone='1334444')

不定长参数

有时, 我们在设计函数时,可能只定了固定数量的参数, 但过了一段时间,有新需求了,需要再加2个参数,这时候你就要改源代码,一改源代码就需要重新进行测试环境、预生成环境的各种严格的测试流程,没问题后,再能再重新部署到生产系统。 如果能一开始设计时就留好后期扩展的空间,就省事了,也提高了程序的可扩展性。

这就可用不定长参数来实现, 共2种方式,*args 和**kwargs

  • 1.*args 元组传值:多给的值 ,都会给到 *args 参数里,以元组形式。
def stu_form(name, age, major, phone, nationality='CN',*args):
    info = f'''
    Name : {name},
    Age  : {age},
    Major: {major},
    Phone: {phone},
    Nation: {nationality}
    '''
    print(info)
    print("不定长列表参数:",args)


stu_form("zhangsan",23,"Finance",15510777777,"US",'lili','Movies') #多写了最后2个参数

输出结果:


    Name : zhangsan,
    Age  : 23,
    Major: Finance,
    Phone: 15510777777,
    Nation: US

不定长列表参数: ('lili', 'Movies')
  • 2.**kwargs 字典传值:接收多余的关键字参数,并以dict形式给到kwargs
def stu_form(name, age, major, phone, nationality='CN',*args,**kwargs):
    info = f'''
    Name : {name},
    Age  : {age},
    Major: {major},
    Phone: {phone},
    Nation: {nationality}
    '''
    print(info)
    print("不定长列表参数args:",args)
    print("不定长列表参数kwargs:",kwargs)

stu_form("zhangsan",23,"Finance",15510099999,"US",'Alex','Movies', hometown="HeNan",university="BeiDaQingNiao") # 多写了hometown, university 2个指定参数

输出结果:


    Name : zhangsan,
    Age  : 23,
    Major: Finance,
    Phone: 15510099999,
    Nation: US

不定长列表参数args: ('Alex', 'Movies')
不定长列表参数kwargs: {'hometown': 'HeNan', 'university': 'BeiDaQingNiao'}
  • 3.直接传列表or dict
    接按下面这种方式 传也可以, 但要加上* 和**符号, 输出效果跟上面一样。
def stu_form(name, age, major, phone, nationality='CN',*args,**kwargs):
    info = f'''
    Name : {name},
    Age  : {age},
    Major: {major},
    Phone: {phone},
    Nation: {nationality}
    '''
    print(info)
    print("不定长列表参数args:",args)
    print("不定长列表参数kwargs:",kwargs)

info = {'hometown':"河南",'university':"北大青鸟"}
hobbies = ["lili",'Movies',"LiveHouse"]
stu_form("zhangsan",23,"Finance",15510099999,"US",*hobbies,**info) 

函数嵌套

def stu_form():

    form = {
        "name": input("Name:").strip(),
        "age": input("Age:").strip(),
        "major": input("Major:").strip(),
        "phone": input("Phone:").strip()
    }

    print(form)
    # 下面边个子函数,是写了一个可以改form dict值 的功能
    def change_form(form_data):  # 这个函数只能在stu_form内调用
        print(form_data.keys())
        print("--------------修改信息--------------")
        while True:
            key = input("输入要改的key>:").strip()
            if not key:continue
            if key in form_data.keys():
                print(f"({key})的当前值{form_data[key]}")
                key_new_val = input("输入要改的新值:").strip()
                form_data[key] = key_new_val
                break
            else:
                print("不合法的key...")

    change_form(form) # 内部调用 
    print("new form:",form) 

    return form

stu_form()

全局变量 VS 局部变量

在函数内部声明的变量叫局部变量, 它只在函数内部生效,函数执行结束后,该变量也行将消亡

def change_name():
    name = "lisi" # 局部变量,只在函数内部生效
    print("in func:", name)

name = "张三"  # 全局变量, 整个代码文件全局生效

change_name()
print("global var:",name)

输出结果:

in func: lisi
global var: 张三

为什么在函数内部改了name的值后, 在外面print的时候却没有改呢? 因为这两个name根本不是一回事

  • 在函数中定义的变量称为局部变量,在程序的一开始定义的变量称为全局变量。
  • 全局变量作用域(即有效范围)是整个程序,局部变量作用域是定义该变量的函数。
  • 变量的查找顺序是局部变量>全局变量
  • 当全局变量与局部变量同名时,在定义局部变量的函数内,局部变量起作用;在其它地方全局变量起作用。
  • 在函数里是不能直接修改全局变量的

目前函数内部的name变量, 和外部的name没任何关系 , 是存活在2个内存空间里的。

如果想在函数内部,修改外面的name变量怎么办呢?

globa声明全局变量

globa 语法告诉解释器, 我要在函数内部引用并修改全局变量

def change_name():
    global name # 声明要引用全局变量
    name = "lisi" # 局部变量,只在函数内部生效
    print("in func:", name)

name = "张三"  # 全局变量, 整个代码文件全局生效

输出结果:

in func: lisi
global var: lisi

匿名函数lambda

匿名函数就是不需要显式的指定函数名的函数

func = lambda x,y:x*y  
res = func(2,3)
print(res) # 6

相当于传统函数

def func(x,y):
  return x*y 

注意lambda后边的代码,一般只写一行,不能像传统函数一样,写很多行。

匿名函数有啥用?

就是为了省事,有的时候 只为了实现个简单的功能,懒得再单独写一个函数,就可用匿名函数。 一般会搭配在各种其它方法里使用。

示例,结合内置函数map的使用:

def add_one(n):
  return n+1
li = [9,11,2,3,4,8,19,12,30]
print(list(map(add_one, li))) # 给列表里每个值+1 , 用传统函数

print(list(map(lambda n:n+1, li))) # 给列表里每个值+1

打印结果:

[10, 12, 3, 4, 5, 9, 20, 13, 31]
[10, 12, 3, 4, 5, 9, 20, 13, 31]

高阶函数

变量可以指向函数名,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。

只需满足以下任意一个条件,即是高阶函数:

  • 接受一个或多个函数作为输入
  • return 返回另外一个函数

示例:

def get_abs(n):
    if n < 0 :
        n = int(str(n).strip("-"))
    return n
  
def add(x,y,f):
    return f(x) + f(y)
res = add(3,-6,get_abs)
print(res) # 9

函数的递归

如下示例,求100不断除以2直到商为0为止,打印每次除的商, 用循环实现:

n = 100
while n > 0:
    n = int(n/2)
    print(n)

输出结果:

50
25
12
6
3
1
0

用函数实现:

def divide_calc(n):
    print(n)
    if n > 0 :
        divide_calc(int(n/2))
    return n
res = divide_calc(10)
print('结果:',res)

结果:

10
5
2
1
0
结果: 10

函数在每进入下一层的时候,当前层的函数并未结束,它必须等它调用的下一层函数执行结束返回后才能继续往下走。 所以最下面的那句return n会等最里层的函数执行时才会执行,然后不断往外退层,所以最后一次的return n执行的是最外层调用函数的, 那个n一开始就是10

递归特性:

  • 必须有一个明确的结束条件()
  • 每次进入更深一层递归时,问题规模相比上次递归都应有所减少
  • 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)

递归在特定场景下还是挺有用的,以后学的一些算法就得用到递归,比如堆排、快排等.

练习:用递归实现2分查找的算法,以从列表 a = [1,3,4,6,7,8,9,11,15,17,19,21,22,25,29,33,38,69,107] 查找指定的值。

def b_search(start,end,li,find_n):

    mid_index = int( (start + end ) / 2 ) # 每次找到列表中间的位置的索引
    if len(li[start:end]) == 1: # 碰头了,查完了
        if li[start] != find_n: # 完了,找不到了
            print("找不到该值.")
            return
        else:
            print("找到了,",li[start])
            return
    if li[mid_index] > find_n: # the val you looking for should be in the left part
        print("去左边一半的数据里查:",li[mid_index], li[start:mid_index])
        b_search(start,mid_index,li,find_n)
    else: # val in right
        print("去右边一半的数据里查:",li[mid_index], li[mid_index:end])
        b_search(mid_index,end,li,find_n)

b_search(0,len(a)-1,a,38)

打印结果:

去右边一半的数据里查: 17 [17, 19, 21, 22, 25, 29, 33, 38, 69]
去右边一半的数据里查: 25 [25, 29, 33, 38, 69]
去右边一半的数据里查: 33 [33, 38, 69]
去右边一半的数据里查: 38 [38, 69]
去左边一半的数据里查: 69 [38]
找到了, 38

内置函数

abs # 求绝对值
all #Return True if bool(x) is True for all values x in the iterable.If the iterable is empty, return True.
any #Return True if bool(x) is True for any x in the iterable.If the iterable is empty, return False.
ascii #Return an ASCII-only representation of an object,ascii(“中国”) 返回”‘\u4e2d\u56fd’”
bin #返回整数的2进制格式
bool # 判断一个数据结构是True or False, bool({}) 返回就是False, 因为是空dict
bytearray # 把byte变成 bytearray, 可修改的数组
bytes # bytes(“中国”,”gbk”)
callable # 判断一个对象是否可调用
chr # 返回一个数字对应的ascii字符 , 比如chr(90)返回ascii里的’Z’
classmethod #面向对象时用,现在忽略
compile #py解释器自己用的东西,忽略
complex #求复数,一般人用不到
delattr #面向对象时用,现在忽略
dict #生成一个空dict
dir #返回对象的可调用属性
divmod #返回除法的商和余数 ,比如divmod(4,2),结果(2, 0)
enumerate #返回列表的索引和元素,比如 d = [“alex”,”jack”],enumerate(d)后,得到(0, ‘alex’) (1, ‘jack’)
eval #可以把字符串形式的list,dict,set,tuple,再转换成其原有的数据类型。
exec #把字符串格式的代码,进行解义并执行,比如exec(“print(‘hellworld’)”),会解义里面的字符串并执行
exit #退出程序
filter #对list、dict、set、tuple等可迭代对象进行过滤, 例子:filter(lambda x:x>10,[0,1,23,3,4,4,5,6,67,7])过滤出所有大于10的值
float #转成浮点
format #没用
frozenset #把一个集合变成不可修改的
getattr #面向对象时用,现在忽略
globals #打印全局作用域里的值
hasattr #面向对象时用,现在忽略
hash #hash函数
help
hex #返回一个10进制的16进制表示形式,hex(10) 返回’0xa’
id #查看对象内存地址
input
int
isinstance #判断一个数据结构的类型,比如判断a是不是fronzenset, isinstance(a,frozenset) 返回 True or False
issubclass #面向对象时用,现在忽略
iter #把一个数据结构变成迭代器,讲了迭代器就明白了
len
list
locals  # 打印所有的局部变量
map # map(lambda x:x**2,[1,2,3,43,45,5,6,]) 输出 [1, 4, 9, 1849, 2025, 25, 36]
max # 求最大值
memoryview # 一般人不用,忽略
min # 求最小值
next # 生成器会用到,现在忽略
object #面向对象时用,现在忽略
oct # 返回10进制数的8进制表示
open
ord # 返回ascii的字符对应的10进制数 ord(‘a’) 返回97,
print
property #面向对象时用,现在忽略
quit
range
repr #没什么用
reversed # 可以把一个列表反转
round #可以把小数4舍5入成整数 ,round(10.15,1) 得10.2
set
setattr #面向对象时用,现在忽略
slice # 没用
sorted # 
staticmethod #面向对象时用,现在忽略
str
sum #求和,a=[1, 4, 9, 1849, 2025, 25, 36],sum(a) 得3949
super #面向对象时用,现在忽略
tuple 
type
vars #返回一个对象的属性,面向对象时就明白了
zip #可以把2个或多个列表拼成一个, a=[1, 4, 9, 1849, 2025, 25, 36],b = [“a”,”b”,”c”,”d”],
  list(zip(a,b)) #得结果 
  [(1, 'a'), (4, 'b'), (9, 'c'), (1849, 'd')]

示例:

# abs 求绝对值
#print(abs(-1)) # 1

# all 判断是否所有元素都为真
# print(all([1, 2, 3])) # True
# print(all([1, 2, 0])) # False
# print(all([1, 2, ''])) # False
# print(all([1, 2, None])) # False
# print(all([1, 2, False])) # False
# print(all([1, 2, True])) # True

# any 判断是否至少有一个元素为真
# print(any([1, 2, 3])) # True
# print(any([1, 2, 0])) # True
# print(any([1, 2, ''])) # True
# print(any([1, 2, None])) # True
# print(any([1, 2, False])) # True
# print(any([1, 2, True])) # True

# bin 返回一个整数的二进制表示形式的字符串
# print(bin(10))  # 输出: 0b1010  
# print(bin(15))  # 输出: 0b1111  
# print(bin(255)) # 输出: 0b11111111

# bool 返回一个布尔值
# print(bool(0)) # 输出: False
# print(bool(1)) # 输出: True


# bytearray 把byte变成 bytearray, 可修改的数组
# b_array = bytearray(b'abc')
# print(b_array[0]) # 输出: 97
# b_array[0] = 98 # 修改
# print(b_array) # bytearray(b'bbc')

# bytes bytes(“中国”,”gbk”)
# s = '中国'
# res1 = s.encode(encoding = 'gbk')
# print(res1) # b'\xd6\xd0\xb9\xfa'
# res2 = bytes(s, 'gbk')
# print(res2) # b'\xd6\xd0\xb9\xfa'


# callable # 判断一个对象是否可调用
# print(callable(print)) # 输出: True
# print(callable('中国')) # 输出: False



# chr # 返回一个数字对应的ascii字符 , 比如chr(90)返回ascii里的’Z’
# print(chr(90)) # 输出: Z
# print(chr(65)) # 输出: A
# print(chr(97)) # 输出: a

# ord # 返回一个字符对应的ascii数字,比如ord('A')返回65
# print(ord('A')) # 输出: 65
# print(ord('Z')) # 输出: 90
# print(ord('a')) # 输出: 97
# print(ord('z')) # 输出: 122

# dict #生成一个空dict
# dir #返回对象的可调用属性
# divmod #返回除法的商和余数 ,比如divmod(4,2),结果(2, 0)

# enumerate #返回列表的索引和元素
# 示例1:枚举列表中的元素及其索引  
# lst = ['a', 'b', 'c', 'd']  
# for index, value in enumerate(lst):  
#     print(f"Index: {index}, Value: {value}")  
# 结果:
# Index: 0, Value: a
# Index: 1, Value: b
# Index: 2, Value: c
# Index: 3, Value: d

# eval #可以把字符串形式的list,dict,set,tuple,再转换成其原有的数据类型。
# print(eval('[1,2,3]')) # 输出: [1, 2, 3]
# print(eval('{1:2,3:4}')) # 输出: {1: 2, 3: 4}


# exec #把字符串格式的代码,进行解义并执行,比如exec(“print(‘hellworld’)”),会解义里面的字符串并执行
# exec("print('helloworld')") # 输出: helloworld
# exec("print(a)") # 输出: a

# exit #退出程序

#filter #对list、dict、set、tuple等可迭代对象进行过滤
#  示例:过滤出所有大于10的值
# res = filter(lambda x:x>10,[0,1,23,3,4,4,5,6,67,7])
# print(list(res)) # 输出: [23, 67]

# float #转成浮点
# print(float(1)) # 输出: 1.0
# print(float(1.1)) # 输出: 1.1
# print(float('1.1')) # 输出: 1.1
# print(float('1')) # 输出: 1.0


# frozenset #把一个集合变成不可修改的
# s = {1, 2, 3}
# res = frozenset(s)
# print(res)
# res.add(4) #  报错:'frozenset' object has no attribute 'add'

# globals 打印全局作用域里的值
# locals  打印所有的局部变量
# def test():
#     a = 1
#     b = 2
#     c = 3
#     # print(globals())
#     print(locals()) # {'a': 1, 'b': 2, 'c': 3}
# test()


# hash #hash函数
# hex #返回一个10进制的16进制表示形式,hex(10) 返回’0xa’
# print(hex(10)) # 输出: 0xa
# print(hex(1)) # 输出: 0x1
# print(hex(11)) # 输出: 0xb

# id #查看对象内存地址

# isinstance 判断一个数据结构的类型,比如判断a是不是fronzenset, isinstance(a,frozenset) 返回 True or False
# a = {1, 2, 3}
# print(isinstance(a, set)) # True
# print(isinstance(a, frozenset)) # False


# min # 求最小值
# max # 求最大值

# reversed 可以把一个列表反转
# lst = [1, 2, 3, 4, 5]
# res = reversed(lst)
# print(list(res)) # [5, 4, 3, 2, 1]
# str = 'abc'
# res = list(reversed(str))
# print(res) # ['c', 'b', 'a']
# print(str[::-1]) # cba

# round #可以把小数4舍5入成整数 ,round(10.15,1) 得10.2
# print(round(10.15, 1)) # 输出: 10.2
# print(round(10.15, 2)) # 输出: 10.15
# print(round(10.15, 3)) # 输出: 10.15
# print(round(10.15, 4)) # 输出: 10.15
# print(round(10.15, 5)) # 输出:10.15


# sorted #对list、dict、set、tuple等可迭代对象进行排序
# 示例:对列表进行排序
# lst = [3, 1, 2, 4, 5]
# res = sorted(lst)
# print(res) # [1, 2, 3, 4, 5]
# #示例:对字典进行排序
# d = {'b': 2, 'a': 1, 'c': 3}
# res = sorted(d.items())
# print(res) # [('a', 1), ('b', 2), ('c', 3)]

# 示例2,我们以成绩进行排序
# scores = [
#   ['zhangsan',99],
#   ['lisi',39],
#   ['tom', 89],
#   ['jack', 59]
# ]
# res = sorted(scores, key=lambda x:x[1],reverse=True)
# print(res) # [['zhangsan', 99], ['tom', 89], ['jack', 59], ['lisi', 39]]

# sum #求和,a=[1, 4, 9, 1849, 2025, 25, 36],sum(a) 得3949

# zip #可以把2个或多个列表拼成一个
# a = [1, 4, 9, 1849, 2025, 25, 36]
# b = ['a','b','c','d']
# print(list(zip(a,b))) # [(1, 'a'), (4, 'b'), (9, 'c'), (1849, 'd')]
# 从上面结果可以看出,zip函数把a和b中的元素一一对应起来,然后生成一个元组,然后生成一个列表,然后返回。

# compile 编译
# f = open("函数递归.py")
# data =compile(f.read(),'','exec')
# exec(data) 

# print
# msg = "又回到最初的起点"
# f = open("tofile","w")
# print(msg,"记忆中你青涩的脸",sep="|",end="",file=f)

⽂件操作

对⽂件的操作有2种,⽂本⽂件、⼆进制⽂件(视频、图⽚等)

open⽅法基本使⽤

open(file, mode='r', encoding=None)

⼏种打开模式:

'r' open for reading (default)
'w' open for writing, truncating the file first(写模式,如果⽂件 在,先清空【危险】)
'x' create a new file and open it for writing(创建模式:如果⽂件在,会报错)
'a' open for writing, appending to the end of the file if it exists(⽇志)
'b' binary mode(2进制模式)
't' text mode (default) (⽂本)
'+' open a disk file for updating (reading and writing)

注:The default mode is 'rt' (open for reading text)

⼀个⽂件对象被open⽅法创建后,这个对象可⽤的有下⾯这些方法:

close       关闭⽂件
closed      查看⽂件是否已关闭
encoding    返回⽂件的编码
flush       把缓存⾥的写⼊的数据强制刷新硬盘
isatty      返回⽂件是否是'interactive'数据流,⽐如是个命令⾏终端,(在unix系统,⼀切皆⽂件)
mode        返回当前⽂件模式
name        返回⽂件名
read        读指定⻓度的内容,f.read(1024) 读1024字节, 不指定参数的话,就读所有内容
readable    ⽂件是否可读
readline    读⼀⾏
readlines   读所有,每⾏列表形式返回
seek        把光标移到指定位置
seekable    该⽂件光标是否可移动
tell        返回当前光标位置
truncate    截断⽂件, f.truncate(100), 从⽂件 开头截断100个字符,后边的都扔掉
writable    是否可写
write       写内容
writelines  把⼀个列表写⼊,每个元素是⼀⾏

创建模式:创建⽂件

f = open("contacts.txt", 'w') # 创建⼀个⽂件对象(⽂件句柄),存为变量f
f.write("zhangsan 133332") # 写⼊
f.write("zhangsan 133332")
f.close() # 关闭这个⽂件
f.write('dddd') # 关闭后,没办法再写⼊了

注意:在 w 模式是创建⼀个⽂件 ,但若该⽂件 已存在,则会清空原⽂件,如果不⾏清空原⽂件,安全起⻅,⽤ x 模式,
若原⽂件存在,会报错,不会直接清空它

读模式:循环读取⽂件&查找

如下有一个model_contacts.txt文件,文件内容如下:

姓名 地区 身⾼ 体重 电话
况咏蜜 北京 171 48 13651054608
王⼼颜 上海 169 46 13813234424
⻢纤⽻ 深圳 173 50 13744234523
乔亦菲 ⼴州 172 52 15823423525
罗梦⽵ 北京 175 49 18623423421
刘诺涵 北京 170 48 18623423765
岳妮妮 深圳 177 54 18835324553
贺婉萱 深圳 174 52 08933434452
叶梓萱 上海 171 49 18042432324
杜姗姗 北京 167 49 13324523342

按⾏读取&循环:

f = open('../../gitee/python/file/model_contacts.txt','rt',encoding='utf-8') # 默认rt模式
# data = f.read() # 读取所有内容
# print(data)
print(f.readline()) # 读取一行
print(f.readline()) # 读取二行
print(f.readline()) # 读取三行

print('----循环读后⾯的-----')
for line in f:
 print(f.readline())

结果如下:

姓名 地区 身⾼ 体重 电话

况咏蜜 北京 171 48 13651054608

王⼼颜 上海 169 46 13813234424

----循环读后⾯的-----
乔亦菲 ⼴州 172 52 15823423525

刘诺涵 北京 170 48 18623423765

贺婉萱 深圳 174 52 08933434452

杜姗姗 北京 167 49 13324523342

打开⽂件后,光标位置在⽂件开头,每读⼀⾏,光标向下移动⼀⾏,因此可以⼀⾏⾏的往后读,已读了的内容不会重复被读
取。

读取指定字符个数:

f = open('../../gitee/python/file/model_contacts.txt','rt',encoding='utf-8') # 默认rt模式
print(f.read(2)) # 读取2个字符  姓名

注意:在⽂本模式下,这个2是代表2个字符,在2进制模式,这个2是指2个字节

⽂件⾥查找内容:

要想在⽂件内找某个词,并打印出所在⾏,只能循环⼀遍⽂件,每⾏依次判断⼀下

for line in f:
 if "梦⽵" in line:
    print(line) # 罗梦⽵ 北京 175 49 18623423421

追加模式

只会往⽂件最后追加,⼀般⽤于写⽇志的场景

f.write("张三 北京 165 50 18834252322\n")
f.write("张三 北京 165 50 18834252322\n")

最终,我们看到txt文件的内容新增了2条:

姓名 地区 身⾼ 体重 电话
况咏蜜 北京 171 48 13651054608
王⼼颜 上海 169 46 13813234424
⻢纤⽻ 深圳 173 50 13744234523
乔亦菲 ⼴州 172 52 15823423525
罗梦⽵ 北京 175 49 18623423421
刘诺涵 北京 170 48 18623423765
岳妮妮 深圳 177 54 18835324553
贺婉萱 深圳 174 52 08933434452
叶梓萱 上海 171 49 18042432324
杜姗姗 北京 167 49 13324523342
张三 北京 165 50 18834252322
张三 北京 165 50 18834252322

注意: 追加模式,即使通过f.seek()把光标移到其它位置 ,再f.write()的时候 ,依然是写到最后的。但是f.seek(10),然后再f.truncate(),会实现⽂件截断,只保留10个字符

flush、seek、tell、truncate

flush:把缓存⾥的写⼊的数据强制刷新硬盘

f = open('flush_txt.txt','w',encoding='utf-8') 
f.write('helloworld welcome to python\n')
f.write('helloworld welcome to python\n')
f.flush()

最后生成一个flush_txt.txt文件,文件内容如下:

f = open('flush_txt.txt','w',encoding='utf-8') 
f.write('helloworld welcome to python\n')
f.write('helloworld welcome to python\n')
f.flush()
  • seek 把光标移到指定位置
  • tell 返回当前光标位置
  • truncate 截断⽂件, f.truncate(100), 从⽂件 开头截断100个字符,后边的都扔掉(默认不传值,就是从光标后面的内容都被截断)
f = open('truncate_txt.txt','w',encoding='utf-8') 
f.write('helloworld welcome to python\n')
f.write('helloworld welcome to python\n')
f.seek(10)
print(f.tell()) # 10
f.truncate() # 结果truncate_txt.txt文件内容只剩下 helloworld

修改文件

r+是修改模式

直接调⽤f.write(),会从开头开始写, 然后会往后覆盖... 如果只覆盖了后边某个⽂字的⼀半,就会出现 乱码

seek是移动字节
tell返回字节数

想把数据源⾥的 罗梦⽵ 换成 张三呀123 , 怎么搞?

例如update.txt文件:

姓名 地区 身⾼ 体重 电话
况咏蜜 北京 171 48 13651054608
王⼼颜 上海 169 46 13813234424
⻢纤⽻ 深圳 173 50 13744234523
乔亦菲 ⼴州 172 52 15823423525
罗梦⽵ 北京 175 49 18623423421
刘诺涵 北京 170 48 18623423765
岳妮妮 深圳 177 54 18835324553
贺婉萱 深圳 174 52 08933434452
叶梓萱 上海 171 49 18042432324
杜姗姗 北京 167 49 13324523342

update.py:

f = open('../../gitee/python/file/update.txt', 'r+',encoding='utf-8')

data = f.read()

# 备份
back_file = open('update.txt.bak','w',encoding='utf-8')
back_file.write(data)
back_file.close()

# 更新
data_new = data.replace('罗梦⽵','张三呀123')
f.seek(0) # 光标移到0
f.truncate() # 清空
f.write(data_new)  # 写⼊新内容
f.close()

# 删除
import os
os.remove('update.txt.bak')

处理不同编码的文件

比如我在mac系统打开window编码的gbk文件,给open⽅法也指定 encoding="gbk" ,这样,就会按gbk来读⽂件内容:

f = open("from_win/file_gbk2.txt",'r',encoding="gbk")
print(f.read())

2进制模式与编解码

遇到图⽚、视频等⾮⽂本⽂件, 该如何处理呢?直接⽤⽂本模式操作的话,会报错.

因为什么utf-8、gbk呀,只是⽂本的编码,肯定不适⽤于图⽚。

⼆进制读⽂件

所以处理⾮⽂本⽂件,就可直接⽤⼆进制模式,以2进制制形式读取数据,不进⾏解码...

f = open('../../gitee/python/file/icon.jpg', 'rb')

for line in f:
  print(line)

打印结果:

b'\n'
b'\t\x08\t\t\n'
b'\x0c\x0f\x0c\n'
b'\x0b\x0e\x0b\t\t\r\x11\r\x0e\x0f\x10\x10\x11\x10\n'
b'\x0c\x12\x13\x12\x10\x13\x0f\x10\x10\x10\xff\xdb\x00.....

打印这个图⽚的内容看到,都是字节类型。

⼆进制写⽂件

当以2进制模式创建⼀个⽂件时,那你只能写⼆进制数据,不能写⽂本啦

f = open('../../gitee/python/file/二进制写文件.txt', mode='wb')
s = '你好,中国'

s_utf8 = s.encode('utf-8') # utf-8编码成字节类型
s_gbk = s.encode('gbk')
f.write(s_utf8)

print('utf-8:',s_utf8)

print('解码:',s_utf8.decode('utf-8')) # 解码: 你好,中国
print('解码:',s_gbk.decode('gbk')) #  解码: 你好,中国

编码转换

以后我们在开发⽹络程序时,如果远程⼀台电脑发送过来的消息是GBK的, 你的电脑是utf-8的,你想把对⽅的消息正确显
示,就要按gbk来 decode 解码

image

posted @ 2024-09-16 09:30  风雨后见彩虹  阅读(39)  评论(0编辑  收藏  举报