不知不觉已经学习到了这个阶段。很高兴自己的努力已经得到了很多收获。
这次,将会继续学习Python相关内容,学习之前没有涉及到的高级用法。
为了更好地理解,应当知道Python有六种标准数据类型。
根据是否可变进行分类,分类如下:
也就是说,字符串确定了就不能变,元组也是,数字亦然。
如果想改变,就得新建一个这个类型的变量,在新的地址创建新的引用。
而列表、字典、集合不是这样。它们可以随时添加新的内容或者删除已有内容,不改变原有的引用。
除了之前一直用的format函数,我们概览一下可能遇到的几种字符串格式化方法。
使用和C语言基本一样的格式字符串。如下:
name = "xjh"
age = 20
score = 114.514
s = "我叫 %s , 今年 %d 岁了,考了 %lf 分。" % (name,age,score)
print(s)
我叫 xjh , 今年 20 岁了,考了 114.514000 分。
这个是在Python 2.6开始提供的一种方法,增强了字符串格式化的功能。
详细的格式化方法在第三章详细讲解过。
用到的时候随时参考就行。第三章里面有例子。
之前没见过的用法:可以在槽里面直接指定索引:
d = {'name':"xjh","age":"20"}
s = "我叫{name} , 今年{age}岁。".format(**d)
print(s)
ls = ["xjh",20]
# 0[x] 中的0是必须的
s = "我叫{0[0]} , 今年{0[1]}岁。".format(ls)
print(s)
我叫xjh , 今年20岁。 我叫xjh , 今年20岁。
甚至可以直接在槽里引用class的参数。
这是Python 3.6中新增加的功能。
f开头,然后是字符串,之后的表达式用{}括起来,会直接把计算后的表达式的值放进去。
name = "xjh"
s = f"我叫{name}"
print(s)
print(f"{114.514+1919810}")
print("{0}".format(114.514+1919810))
print(f"{114.514+1919810=}")
我叫xjh 1919924.514 1919924.514 114.514+1919810=1919924.514
s = {1,1,4,5,1,4}
s_new = {i**2 for i in s if i>=2}
print(s_new)
{16, 25}
a = {x for x in "abdsdkfjfsgavcbdfgs" if x not in "abc"}
print(a)
{'v', 'k', 'f', 's', 'g', 'j', 'd'}
a = {x for x in range(10)}
print(a)
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
a = (i for i in range(10))
print(a) # 是一个生成器对象
a = tuple(a)
print(a)
<generator object <genexpr> at 0x106f81850> (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
甚至表达式可以是一个有返回值的函数,都行。
过滤掉长度小于或等于3的字符串列表,并将剩下的转换成大写字母:
names = ['Bob','Tom','alice','Jerry','Wendy','Smith']
new_names = [name.upper() for name in names if len(name)>3]
print(new_names)
['ALICE', 'JERRY', 'WENDY', 'SMITH']
生成一个网站名到长度的字典:
web_list = ['Google','Apple','Bilibili','Wikipedia','Caiguu']
new_dict = {key:len(key) for key in web_list}
print(new_dict)
{'Google': 6, 'Apple': 5, 'Bilibili': 8, 'Wikipedia': 9, 'Caiguu': 6}
生成一个数字-平方数的字典:
num = [1,2,3,4,5,6,7,8,9,10]
num_dict = {n:n**2 for n in num}
print(num_dict)
{1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81, 10: 100}
import sys
print('命令行参数如下:')
for i in sys.argv:
print(i)
print('\n\nPython 路径为:', sys.path, '\n')
命令行参数如下: /opt/homebrew/lib/python3.10/site-packages/ipykernel_launcher.py -f /Users/caiguu/Library/Jupyter/runtime/kernel-ed58cdb5-333b-4dc1-8c32-30ce0c9f0f36.json Python 路径为: ['/Applications/PyCharm.app/Contents/plugins/python/helpers-pro/jupyter_debug', '/Applications/PyCharm.app/Contents/plugins/python/helpers/pydev', '/Users/caiguu', '/opt/homebrew/Cellar/python@3.10/3.10.1/Frameworks/Python.framework/Versions/3.10/lib/python310.zip', '/opt/homebrew/Cellar/python@3.10/3.10.1/Frameworks/Python.framework/Versions/3.10/lib/python3.10', '/opt/homebrew/Cellar/python@3.10/3.10.1/Frameworks/Python.framework/Versions/3.10/lib/python3.10/lib-dynload', '', '/opt/homebrew/lib/python3.10/site-packages', '/opt/homebrew/opt/python-tk@3.10/libexec', '/opt/homebrew/lib/python3.10/site-packages/IPython/extensions', '/Users/caiguu/.ipython']
一个模块只会被导入一次,不管你执行了多少次。
会从sys.path中搜索对应的模块。如果没有,就会报错。
函数也可以赋值,只不过是给了个别名而已。
每个模块都有独立的符号表,全局变量只在模块内部是全局的。
如果你import了一个包,那么会自动执行其中的主程序。
如果你只希望自己才执行主程序,那么请使用:
if __name__ == '__main__':
print("我被自己调用了")
else:
print("我被其他人引用了")
这样,自己运行自己的时候,执行main里面的。如果被引用,执行其他的。
每个模块都有一个__name__属性,当其值是'__main__'时,表明该模块自身在运行,否则是被引入。
可以用dir函数来找到模块内定义的所有名称。
是一个字符串列表。
文件夹就是包!用点来分隔,和Java中差不多。
导入一个包的时候,会根据sys.path的目录来寻找对应的子目录。
目录只有包含了__init__.py才会被认作是一个包。
所以from package import 包
就是这个意思!
如果你想要指定from package import *的时候导入谁,在init文件里边加一个__all__列表,字符串的形式告诉需要导入哪些即可。
迭代器很强大!能够用来访问集合元素。
迭代器是一个对象,能够记住遍历的位置。它只能前进不能后退。
迭代器需要掌握两个方法:iter()和next()
字符串、列表、元组对象都可以用于创建迭代器。
my_list = [1,1,4,5,1,4]
it = iter(my_list)
print(it)
print(next(it))
print(next(it))
print(next(it))
print(next(it))
it = iter(my_list)
for i in it:
print(i,end=",")
<list_iterator object at 0x105219c00> 1 1 4 5 1,1,4,5,1,4,
import sys # 引入 sys 模块
my_list=[1,2,3,4]
it = iter(my_list) # 创建迭代器对象
while True:
try:
print (next(it))
except StopIteration:
print("直接来吧!我滴任务完成啦!")
break
1 2 3 4 直接来吧!我滴任务完成啦!
如果实现了__iter__()和__next__(),那么就是一个迭代器。
__iter__()
:返回一个特殊的迭代器对象,实现了__next__()
并通过StopIteration
异常标识迭代的完成。
__next__()
:返回下一个迭代器对象。
我们创建一个迭代器,数字的迭代器。初始值为1,逐步增加1。
为了防止无限迭代,我们指定20次抛出StopIteration异常。
class MyNumbers:
def __iter__(self):
self.a = 1
return self
def __next__(self):
if self.a > 20:
raise StopIteration
else:
#返回当前的,同时位置+1
x = self.a
self.a += 1
return x
myClass = MyNumbers()
myIter = iter(myClass)
for i in myIter:
print(i)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
def fibonacci(n): # 生成器函数 - 斐波那契
a, b, counter = 0, 1, 0
while True:
if counter > n:
return
yield a
a, b = b, a + b
counter += 1
f = fibonacci(10) # f 是一个迭代器,由生成器返回生成
while True:
try:
print (next(f), end=" ")
except StopIteration:
break
0 1 1 2 3 5 8 13 21 34 55
在调试的时候,f是一个generator类型。
遇到了yield就会返回yield那里的表达式。等到了next之后,回到刚才的位置,继续执行。
首先,Python支持多继承。
类的私有属性
class Car:
def __init__(self,name):
self.name = name
self.__price = 0
class GasCar(Car):
def __init__(self,name):
super().__init__(name)
self.__price = 100
# super().__price = 150 会报错
self.a = 15
print("hehe")
c = GasCar("olg")
print(c._GasCar__price)
hehe 100
经过调试,发现一个细节。私有成员在子类中是可以访问到的。
它们有各自的签名,比如_Car__price
和_GasCar_price
而且如果子类试图访问父类的私有变量,是不允许的!
所以它客观存在,但是访问不到。
但是可以从外界暴力访问。但是不要这么干!
最好的办法:通过函数封装起来就好啦!
为什么区分?因为对于不同的类型,操作是不一样的。
让我们康康吧!
def change(a):
print(id(a))
a = 114514
print(id(a))
a = 1
print(id(a))
change(a)
print(id(a))
print(a)
4304617712 4304617712 4566923920 4304617712 1
def changelist(li):
li.append("哼哼哼")
li.append("啊啊啊啊啊啊啊啊啊")
list = ["114514","1919810"]
changelist(list)
print(list)
['114514', '1919810', '哼哼哼', '啊啊啊啊啊啊啊啊啊']
def print_str(str):
print(str)
print_str("哼哼哼 啊啊啊啊啊啊啊啊")
哼哼哼 啊啊啊啊啊啊啊啊
def print_add_str(str1,str2):
s = str1+str2
return s
print(print_add_str("114514","1919810"))
print(print_add_str(str2="1919810",str1="114514"))
1145141919810 1145141919810
def age(age=18):
return age
print(age(10))
print(age())
10 18
传进去以后是一个元组
def print_info( arg1, *var_tuple ):
# 打印任何传入的参数
print ("输出: ")
print (arg1)
print (var_tuple)
# 调用print_info函数
print_info( 70, 60, 50, 114514, 1919810 )
print_info( 80)
输出: 70 (60, 50, 114514, 1919810) 输出: 80 ()
如果不传入,那就是个空的元组。
还有一种,两个星号,这样会以字典的形式导入。
# 可写函数说明
def print_info( arg1, **var_dict ):
print ("输出: ")
print (arg1)
print (var_dict)
# 调用print_info 函数
print_info(1, a=2,b=3)
print_info(1,a114514 = 2, a1919810 = 3)
输出: 1 {'a': 2, 'b': 3} 输出: 1 {'a114514': 2, 'a1919810': 3}
如果单独出现星号,那么后面的参数只能通过关键字才能传入。
def olg(a,b,*,c,d,e,f):
print(f"{a,b,c,d,e,f}")
olg(1,1,d=5,c=4,f=4,e=1)
olg(1,1,4,5,1,4)
(1, 1, 4, 5, 1, 4)
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) /var/folders/nc/rrdwnt790c13mtwryb6prjfc0000gn/T/ipykernel_25322/810105125.py in <module> 3 4 olg(1,1,d=5,c=4,f=4,e=1) ----> 5 olg(1,1,4,5,1,4) TypeError: olg() takes 2 positional arguments but 6 were given
def f(a, b, /, c, d, *, e, f):
print(a, b, c, d, e, f)
f(1,2,3,4,e=5,f=6)
f(1,2,c=3,d=4,e=5,f=6)
1 2 3 4 5 6 1 2 3 4 5 6
将元组(*)解包成位置参数,将字典(**)解包成关键字参数
def print_tuple(a,b,c):
print('a:',a,end=' ')
print('b:',b,end=' ')
print('c:',c)
args=(1,11,111)
print_tuple(*args)
a: 1 b: 11 c: 111
def print_tuple(a,b,c,*olggg):
print('a:',a,end=' ')
print('b:',b,end=' ')
print('c:',c)
print('olg', olggg)
print('olg', *olggg)
args=(1,11,111,114514,1919810,666)
print_tuple(*args)
a: 1 b: 11 c: 111 olg (114514, 1919810, 666) olg 114514 1919810 666
对于字典,同样可以使用**进行解包,解包为value参数
对于*解包字典,就是解包为key参数
def print_dict(a,b,c):
print('a:',a,end=' ')
print('b:',b,end=' ')
print('c:',c)
args={'a':3,'b':2,'c':1}
print_dict(**args)
print_dict(*args)
a: 3 b: 2 c: 1 a: a b: b c: c
巧妙利用,去掉一个最高分和最低分:
将列表解包为多个数字参数
score = [80,84,90,92,98,95,93,100]
score.sort()
print(score)
a , *b , c = score
print(b)
[80, 84, 90, 92, 93, 95, 98, 100] [84, 90, 92, 93, 95, 98]
所以经过如上的所有探索,可以暂时认为*就是扩展序列参数为多个变量
对于字典,**就是解包为值参数,*就是解包为关键字参数
经过数天的Python学习,对于基础语法体系和一些高级应用都有了很好的理解。
希望能够继续努力,不断提升自己的能力。
如果以后遇到了需要整理和总结的内容,也会更新到博客上。
至此,Python的基础学习阶段完结撒花!
See you!