python基础语法菜鸟教程,python基础语法视频教程

本篇文章给大家谈谈python基础语法菜鸟教程,以及python基础语法视频教程,希望对各位有所帮助,不要忘了收藏本站喔。

ps:Python所有的数据都是对象,包括int,float这些都是对象,所以Python万事万物皆对象!!!

1. 基础知识

1.1 变量的定义和使用

变量由三部分组成:

  • 标识:表示地址,用id(obj)获取
  • 类型:表示数据类型,用type(obj)来获取
  • 值:表示具体数据,用print(obj)来打印输出

整数的不同进制表示:

  • 十进制:默认
  • 二进制:以0b开头
  • 八进制:以0o开头
  • 十六进制:以0x开头

数据类型转换:

  • str():将其他类型转换成字符串
  • int():将其他类型转换成整数
  • float():将其他类型转换成浮点数

1.2 注释

  • 单行注释
# 单行注释
  • 多行注释
"""
多行注释
"""
  • 编码声明
#coding:utf-8
#coding:gbk

#encoding=utf-8
#encoding=gbk

1.3 输入

input()函数用接收用户输入,返回值类型为str。

a = int(input("请输入年龄:")); # 用int 将str转换类型

1.4 运算符

  • 算术运算符:**, *, /, //, %, +, -
  • 位运算符:<<, >>, &, |
  • 比较运算符:>, <, >=, <=, ==, !=
  • 逻辑运算符:and, or
  • 赋值运算符:=

2. 程序流程结构

2.1 条件结构

# 单分支结构
if expression:
    pass # 占位符,在搭框架的时候用
# 双分支结构
if expression:
    pass
else:
    pass
# 多分支结构
if expression1:
    pass
elif expression2:
    pass
elif expression3:
    pass
[else:]
    pass
# 嵌套if
if expression1:
    if expression2:
        pass
    else:
        pass
else:
    pass

2.2 循环结构

# while循环
while expression:
    pass
# for-in循环
for item in 'python':
    pass
for i in range(10):
    pass

2.3 流程控制语句

  • break语句:用于结束当前循环体
  • continue语句:用于结束当次循环
  • else语句:
    • if … else …
    • while … else … 和 for … else …:没有碰到break时执行else

3. 容器

在这里插入图片描述

3.1 列表

3.1.1 创建
lst=['hello','world',98]
lst2=list(['hello','world',98])
3.1.2 查询
  • 获取指定元素的索引:内置函数index()
  • 获取单个元素:[index_num]
  • 切片:
# 正向:step为正数
[start : stop [: step]] 
# 反向:step为负数
[start::step]
3.1.3 增加
  • append(element)
  • extend(element)
  • insert(pos, element)
  • 切片
3.1.4 删除
  • remove(element):删除第一个element元素
  • pop([index]):删除指定索引位置的元素,若不指定,则删除最后一个元素
  • 切片
  • del lst:删除列表
  • clear():清空列表
3.1.5 修改
  • 单个
  • 切片
  • 遍历
# 遍历修改
list1 = [12,3,4]
for i in list1:
    i += 5 # 无法修改
print(list1) # [12,3,4]
for i,v in enumerate(list1):
    list1[i] = v + 5
print(list1) # [17, 8, 9]
3.1.6 排序
  • sort()方法:sort(lst, reverse=True),默认升序排列,无返回值
  • sorted()内置函数:sorted(lst, reverse=True),返回排好序的列表

3.2. 字典

3.2.1 创建
scores = {"张三":100, "李四":98}
student = dict(name='jack',age=20)
d = {}
3.2.2 查询
  • [key]
  • get(key[, default])方法
3.2.3 增加/修改
scores['Jack'] = 90
3.2.4 删除
del scores['张三']
3.2.5 获取字典视图
  • keys():返回一个列表,存储所有的key
  • values():返回一个列表,存储所有的value
  • items():返回一个列表,存储所有的(key, value)元组
3.2.6 内置函数zip()

内置函数zip()用于将可迭代的对象作为参数,将对象中对应的元素打包成一个元组,然后返回由这些元组组成的列表python自动化运维系统

# 内置函数zip()
items = ['fruits','books','others']
prices = [96,78,85]
d = {item.upper():price for item,price in zip(items,prices)}
print(d)

3.3. 元组

元组是不可变序列,没有增、删、改的操作。

3.3.1 创建
t1 = ('python','hello',90)
t2 = tuple('python','hello',90)
t3 = (10,) # 只包含一个元素需要使用逗号
3.3.2 遍历

可以用for-in遍历。

3.4. 集合

集合是没有value的字典,因为字典中的key不允许重复,所以集合也不允许有重复元素。

3.4.1 创建
s1 = {'python','hello',90}
s2 = set(range(6))
3.4.2 增加
  • add():一次添加一个元素
  • update():至少添加一个元素
3.4.3 删除
  • remove(element):删除一个指定元素
  • discard(element):删除一个指定元素
  • pop():删除任意一个元素
  • clear():清空
3.4.4 集合间的关系

在这里插入图片描述

s = {10, 20, 30, 40}
s1 = {20, 10, 40, 30}
s2 = {10, 30}
s3 = {30, 50}
print(s2.issubset(s)) # True
print(s3.issubset(s)) # False
print(s.issuperset(s2)) # True
print(s.issuperset(s3)) # False

s4 = {60, 70, 80}
print(s.isdisjoint(s1)) # False
print(s.isdisjoint(s2)) # False
print(s.isdisjoint(s4)) # True
3.4.5 集合间的数学操作

交集

# 交集:intersection()  或  &
s1 = {10, 20, 30}
s2 = {23, 30, 20}
print(s1.intersection(s2)) # {20, 30}
print(s1 & s2) # {20, 30}

# 并集:union()   或  |
print(s1.union(s2)) # {20, 23, 10, 30}
print(s1 | s2) # {20, 23, 10, 30}

# 差集:difference()   或  -
s1 = {10, 20, 30, 40}
s2 = {20, 30, 50}
print(s1.difference(s2)) # {40, 10}
print(s1 - s2) # {40, 10}

# 对称差集:symmetric_difference()   或   ^
s1 = {10, 20, 30, 40}
s2 = {20, 30, 50}
print(s1.symmetric_difference(s2)) # {40, 10, 50}
print(s1 ^ s2) # {40, 10, 50}

4. 字符串

在这里插入图片描述

4.1 驻留机制

在python中字符串是不可变的字符序列,python机制有一个驻留池,用于存放字符串,当创建字符串时,编译器会把字符串的地址赋给新创建的变量。这个类似于c++的常量池。

a='Python'
b='Python'
c='python'
print('a:',a,id(a)) # a: Python 2834106576176
print('b:',b,id(b)) # b: Python 2834106576176
print('c:',c,id(c)) # c: python 2752480713056

4.2 查询方法

  • index(substr):查询substr第一次出现的位置,若不存在,报错
  • rindex(substr):查询substr最后一次出现的位置,若不存在,报错
  • find(substr):查询substr第一次出现的位置,若不存在,返回-1
  • rfind(substr):查询substr最后一次出现的位置,若不存在,返回-1

4.3 大小写转换方法

  • upper()
  • lower()
  • swapcase():大写变小写,小写变大写
  • capitalize():首字符大写,其余小写
  • titile():每个单词首字符大写,其余小写

转换之后返回新字符串

4.4 对齐方法

  • center(宽度,填充符):居中对齐,指定填充符,默认空格填充。
  • ljust(宽度,填充符):左对齐,指定填充符,默认空格填充。
  • rjust(宽度,填充符):右对齐,指定填充符,默认空格填充。
  • zfill(宽度 ):右对齐,零填充
s='hello,Python'
print(s.center(20,'*')) # ****hello,Python****
print(s.ljust(20,'*')) # hello,Python********
print(s.ljust(10,'*')) # hello,Python
print(s.ljust(20)) # hello,Python

print(s.rjust(20,'*')) # ********hello,Python
print(s.rjust(20)) #         hello,Python
print(s.rjust(10)) # hello,Python

print(s.zfill(20)) # 00000000hello,Python
print('-9010'.zfill(8)) # -0009010

4.5 切分方法

  • split(sep=’ ', maxsplit=-1):从左边开始切分,返回一个列表
  • rsplit(sep=’ ', maxsplit=-1):从右边开始切分,返回一个列表
s='hello world python'
print(s.split()) # ['hello', 'world', 'python']

s1='hello|world|python'
print(s1.split('|')) # ['hello', 'world', 'python']
print(s1.split('|',maxsplit=1)) # ['hello', 'world|python']

s='hello world python'
print(s.rsplit()) # ['hello', 'world', 'python']

s1='hello|world|python'
print(s1.rsplit('|')) # ['hello', 'world', 'python']
print(s1.rsplit('|',maxsplit=1)) # ['hello|world', 'python']

4.6 替换和合并方法

  • replace(old, new, count=None):count指定最大替换次数
  • join(iterable):将列表或元组中的字符串合并成一个字符串
s = 'hello,python,python,python'
print(s.replace('python', 'c++')) # hello,c++,c++,c++
s = 'hello,python,python,python'
print(s.replace('python', 'c++', 2)) # hello,c++,c++,python

lst = ['hello', 'java', 'python'] 
print('|'.join(lst)) # hello|java|python
print(''.join(lst)) # hellojavapython

t = ('hello', 'java', 'python')
print('|'.join(t)) # hello|java|python
print(''.join(t)) # hellojavapython

print('*'.join('Python')) # P*y*t*h*o*n

4.7 拼接方法

  • join():跟合并是一样的,官方推荐拼接用join(),而不用’+'。

原因:String对象是定长对象,一旦创建,长度就不可变化,若是使用+号连接两个字符串,则会新开辟一段长度总和长度的内存,再将两个字符串memcpy进去。如果要连接N个String对象,则要进行N-1次内存申请和拷贝。join() 方法对于连接一个list或tuple中的元素非常有效,它会先统计所有元素的长度,申请内存,然后拷贝。

4.8 比较操作

>, >=, <, <=, ==, !=

# 底层遍历两个字符串的每个字符,调用ord()得到ascii码值,比较两者的码值。

# 与ord()对应的是chr(),chr()将ascii码值转换为对应的字符。

==is的区别:

  • == 比较的是字符串的值
  • is 比较的是字符串的id

4.9 格式化字符串

有三种方法:

  • %(和C一样)
  • format
  • f-string

在这里插入图片描述

name = '张三'
age = 20
print('我叫%s,今年%d岁' % (name, age)) # 我叫张三,今年20岁
print('我叫{0},今年{1}岁了'.format(name, age)) # 我叫张三,今年20岁了
print(f'我叫{name},今年{age}岁!')  # 字符串前加f代表格式化字符串

设置宽度和精度:

print('%10d' % 99) # 宽度
print('0123456789')
print('%.3f'%3.1415926) # 保留小数
print('%10.3f'%3.1415926)
#         99
# 0123456789
# 3.142
#      3.142
print('{0:.3}'.format(3.1415926)) # :.3表示一共是三位数,0代表的是format中的第0位
print('{0:.3f}'.format(3.1415926)) # :.3f表示三位小数
print('{0:10.3f}'.format(3.1415926)) # 同时设置宽度和精度
# 3.14
# 3.142
#      3.142

最后一种格式化字符串的方法是使用以f开头的字符串,称之为f-string,它和普通字符串不同之处在于,字符串如果包含{xxx},就会以对应的变量替换:

>>> r = 2.5
>>> s = 3.14 * r ** 2
>>> print(f'The area of a circle with radius {r} is {s:.2f}')
The area of a circle with radius 2.5 is 19.62

4.10 编码转换

  • 编码:将字符串转换位二进制数据
  • 解码:将二进制数值转换成字符串类型
s='天涯共此时'
print(s.encode(encoding='GBK')) # 一个中文占两个字节
print(s.encode(encoding='UTF-8')) # 一个中文占三个字节
# b'\xcc\xec\xd1\xc4\xb9\xb2\xb4\xcb\xca\xb1'
# b'\xe5\xa4\xa9\xe6\xb6\xaf\xe5\x85\xb1\xe6\xad\xa4\xe6\x97\xb6'
 
#解码
byte=b'\xcc\xec\xd1\xc4\xb9\xb2\xb4\xcb\xca\xb1'
print(byte.decode(encoding='GBK'))
byte=b'\xe5\xa4\xa9\xe6\xb6\xaf\xe5\x85\xb1\xe6\xad\xa4\xe6\x97\xb6'
print(byte.decode(encoding='UTF-8'))
# 天涯共此时
# 天涯共此时

5. 函数

在这里插入图片描述

5.1 函数的参数传递

参数分为不可变类型和可变类型:

  • 不可变类型:包括数字,字符串,元组,不可变集合。在函数体内的修改不会影响实参的值。
  • 可变类型:包括列表,字典。在函数体内的修改会影响实参的值。

Python的函数参数传递是引用传递,记住这一点,我们来解决下面的三个问题:

问题1:在函数内部,针对参数使用 赋值语句,会不会影响调用函数时传递的 实参变量?——不会!

  • 无论传递的参数是 可变 还是 不可变
def demo(num,num_list):
    print('函数内部的代码')
    # 在函数内部,针对参数使用赋值语句,不会修改到外部的实参变量
    num = 100
    num_list = [1,2,3] # 赋值语句实际上修改了局部变量num_list的引用
    print(num)
    print(num_list)
    print('函数执行完成')

gl_num = 90
gl_list = [4,5,6]
demo(gl_num,gl_list)
print(gl_num)
print(gl_list)

输出:

函数内部的代码
100
[1, 2, 3]
函数执行完成
90
[4, 5, 6]

问题2:如果传递的参数是 可变类型,在函数内部,使用 方法 修改了数据的内容,同样会影响到外部的数据

def demo(num_list):
    print('函数内部的代码')
    # 使用方法修改列表的内容
    num_list.append(9) # 没修改局部变量num_list的引用
    print(num_list)
    print('函数执行完成')

gl_list = [1,2,3]
demo(gl_list)
print(gl_list)

输出:

函数内部的代码
[1, 2, 3, 9]
函数执行完成
[1, 2, 3, 9]

问题3:在 Python 中,列表变量调用 += 本质上是在执行列表变量的 extend 方法,不会修改变量的引用。

def demo(num,num_list):
    print('函数内部的代码')
    # num += num + num
    num += num
    # num_list += num_list
    num_list += num_list
    print(num)
    print(num_list)
    print('函数执行完成')

gl_num = 9
gl_list = [1,2,3]
demo(gl_num,gl_list)
print(gl_num)
print(gl_list)

输出:

函数内部的代码
18
[1, 2, 3, 1, 2, 3]
函数执行完成
9
[1, 2, 3, 1, 2, 3]

5.2 函数的返回值

  • 如果没有返回值,return 可以不写
  • 如果返回一个值,返回的类型是原值类型
  • 如果返回多个值,返回的类型是元组

5.3 函数的参数定义

  • 个数可变的位置参数
    • 位置参数的结果是一个元组
    • 一个函数最多只能有一个位置参数
def fun(*args):
    print(args)

fun(10) # (10,)
fun(10,20,30) # (10, 20, 30)
  • 个数可变的关键字形参
    • 关键字形参的结果是一个字典
    • 一个函数最多只能有一个关键字参数
def fun(**kwargs):
    print(kwargs)

fun(a=10)
fun(a=10,b=20,c=30)

既有个数可变的位置形参,也有个数可变的位置形参时,个数可变的位置形参要放在个数可变的关键字形参之前

上面简单介绍了个数可变的位置参数和个数可变的关键字参数,其实这个传参过程是元组和字典的拆包和装包过程:

  • 装包:把传递的参数,包装成一个集合,称之为“装包”。
  • 拆包:把集合参数,再次分解成单独的个体,称之为“拆包”。

下面我们来看看例子:

def mySum(a,b,c,d):
    print(a+b+c+d)

def test(*args):
    print(args)

    # 拆包,将元组args拆分成多个数字
    print(*args)

    # mySum(args) # 错误,相当于将元组args传给了参数a
    mySum(args[0],args[1],args[2],args[3]) # 这样写比较繁琐
    mySum(*args) # 拆包传参,更加简洁

# 将多个数字装到一个元组args中,称为装包
test(1,2,3,4)
'''
输出:
(1, 2, 3, 4)
1 2 3 4
10
10
'''

def mySum1(a,b):
    print(a)
    print(b)

def test1(**kwargs):
    print(kwargs)

    # 拆包
    # 应该使用 ** 进行拆包操作
    # 拆完之后就是a=1,b=2
    # print(**kwargs) # 错误,这里相当于执行print(a=1,b=2),显然语法不对
    # mySum1(kwargs) # 错误,相当于将字典kwargs传给了参数a
    mySum1(a=1,b=2) # 这样写比较繁琐
    mySum1(**kwargs) # 拆包传参,更加简洁

# 将多个关键字参数装到一个字典kwargs中,称为装包
test1(a=1,b=2)
'''
输出:
{'a': 1, 'b': 2}
1
2
1
2
'''

拆包装包的应用:

def demo(*args,**kwargs):
    print(args)
    print(kwargs)

# 元组变量/字典变量
gl_nums = (1,2,3)
gl_dict = {"name":"小明","age":18}

demo(gl_nums,gl_dict) # 这里传的都是元组变量
print('-------------')
# 拆包语法,简化元组变量/字典变量的传递
demo(*gl_nums,**gl_dict) # 使用拆包,相当于调用demo(1,2,3,name='小明',age=18)
print('-------------')
demo(1,2,3,name='小明',age=18)

输出:

((1, 2, 3), {'name': '小明', 'age': 18})
{}
-------------
(1, 2, 3)
{'name': '小明', 'age': 18}
-------------
(1, 2, 3)
{'name': '小明', 'age': 18}

在这里插入图片描述

5.4 变量的作用域

  • 局部变量:在函数内定义的变量,只有函数内部有效,函数外不能访问。如果使用global声明,这个变量就会变成全局变量,在函数外部也能访问。
  • 全局变量:函数体外定义的变量,可以作用于函数内外。
    • 全局变量如果是不可变类型,在函数中进行修改的时候需要添加 global 关键字
    • 全局变量如果是可变类型,在函数中进行修改的时候不需要添加 global 关键字

详细的示例请看Python 全局变量

num = 10

def f1():
    num = 99 # 这里实际上是在函数内部定义了一个局部变量,不是函数外的num全局变量
    print(num)

def f2():
    print(num)

f1() # 输出99
f2() # 输出10
num = 10

def f1():
    global num # 这里声明了使用函数外的全局变量num,所以这里能够修改num全局变量
    num = 99
    print(num)

def f2():
    print(num)

f1() # 输出99
f2() # 输出99

5.5 匿名函数

格式:

lambda 参数列表: 运算表达式
r = lambda a:a+1
result = r(5)
print(result)

"""
输出:
6
"""

匿名函数不会单独使用,它的使用场合是 高阶函数

  • 一个函数可以作为另一个函数的参数,也可以作为另一个函数的返回值。这种函数的使用方式我们称之为高阶函数
list1 = [('tom',19),('tony',20),('lily',18),('daniel',21),('rose',22)]
m = max(list1,key=lambda x:x[1]) # 按照年龄排序,取最大值
print(m)

s = sorted(list1,key=lambda x:x[1])
print(s)

s = sorted(list1,key=lambda x:x[1],reverse=True)
print(s)

rr = filter(lambda x:x[1] > 20,list1)
print(list(rr))

ma = map(lambda x:x[0].title(),list1)
print(list(ma))

函数式编程的使用可以移步去Python常用内置模块使用

5.6 内部函数

定义在函数内部的函数称为内部函数。

def a(): # 外部函数
    def b(): # 内部函数
        pass

内部函数的特点:

  • 可以访问外部函数的变量。
  • 内部函数可以修改外部函数的可变类型的变量。
  • 内部函数修改全局的不可变的变量时,需要在内部函数中声明 global 变量名;内部函数修改外部函数的不可变的变量时,需要在内部函数中声明 nonlocal 变量名
  • locals() 查看局部变量有哪些,以字典的形式输出;globals() 查看全局变量有哪些,以字典的形式输出(注意里面会有一些系统的键值对)。

5.7 闭包

闭包的概念:

  • 外部函数中定义了内部函数
  • 内部函数中,对外部函数的变量进行引用
  • 外部函数有返回值,且返回值为内部函数
def func(a,b):
    c = 10

    def inner_func():
        s = a + b + c
        print("相加之后的结果是:", s)

    return inner_func

# 调用func
ifunc = func(6,9) # ifunc就是inner_func

# 调用返回来的内部函数
ifunc()

输出:

相加之后的结果是: 25

闭包的作用:

  • 保存返回闭包时的状态(外部函数变量)
  • 可以使用同级的作用域
  • 获取其他元素的内部变量
  • 延长作用域

闭包的缺点:

  • 作用域不直观
  • 因为变量不会呗垃圾回收,所以有一定的内存占用问题

闭包总结:

  • 闭包似优化了变量,原来需要类对象完成的工作,闭包也可以完成
  • 由于闭包引用了外部函数的变量,导致外部函数的变量没有及时释放,造成内存消耗
  • 闭包的好处,使代码变得简洁,便于阅读
  • 闭包是理解装饰器的基础

5.8 装饰器

本质上,装饰器就是一个返回函数的高阶函数。它的作用是在代码运行期间动态增加原函数的功能。

装饰器的特点:

  • 装饰器的参数有函数,这个函数就是要被装饰(扩展)的函数
  • 装饰器是一个闭包

使用装饰器的底层逻辑:

  • 被装饰函数作为参数传递给装饰器
  • 执行装饰器函数
  • 装饰器函数的返回值又赋值给被装饰函数
"""
如果要扩展原函数house的功能,一般的做法是新定义一个函数house1,然后在新函数里进行扩展。
但这种做法的坏处是需要修改程序中所有调用house的语句为house1,比较麻烦。
"""
# # 原函数
# def house():
#     print("我是毛坯房")
#
# def house1():
#     house()
#     print("----->刷漆")
#     print("----->铺地板")
#     print("----->装门")
#
# house1()

"""
用装饰器可以方便的扩展原函数house的功能,而且无需改变原函数的名字
"""
# 定义一个装饰器
def decorator(func):
    a = 100
    print("wrapper外层打印测试")

    def wrapper():
        func()
        print("----->刷漆")
        print("----->铺地板")
        print("----->装门")

    print("wrapper加载完成......")
    return wrapper

# 使用装饰器
@decorator
def house():
    print("我是毛坯房")

print(house)
house()

输出:

wrapper外层打印测试
wrapper加载完成......
<function decorator.<locals>.wrapper at 0x000001DF2B085840>
我是毛坯房
----->刷漆
----->铺地板
----->装门

注意,当被装饰函数带有参数时,装饰器中的wrapper函数必须有相应的参数(一般用可变参数)。这是因为使用装饰器时被装饰函数被装饰器中返回的wrapper函数赋值。

import time

def decorator(func):
    def wrapper(*args, **kwargs):
        print("正在校验中...")
        time.sleep(2)
        print("校验完毕...")
        # 调用原函数
        func(*args, **kwargs)

    return wrapper

@decorator
def f1(n):
    print("-------f1-------", n)

f1(5)

@decorator
def f2(name, age):
    print("-------f2-------", name, age)

f2('lily', 20)

@decorator
def f3(students, clazz='1905'):
    print("{}班级的学生如下:".format(clazz))
    for stu in students:
        print(stu)

students = ['lily', 'tom', 'lucy']
f3(students)

@decorator
def f4():
    print("-------f4-------")

f4()

输出:

正在校验中...
校验完毕...
-------f1------- 5
正在校验中...
校验完毕...
-------f2------- lily 20
正在校验中...
校验完毕...
1905班级的学生如下:
lily
tom
lucy
正在校验中...
校验完毕...
-------f4-------

可以连续使用多个装饰器来装饰一个函数,优先使用离被装饰函数近的装饰器。

# 装饰器1
def decorator1(func):
    print("------>1 start")

    def wrapper(*args, **kwargs):
        func()
        print("----->刷漆")

    print("------>1 end")
    return wrapper

# 装饰器2
def decorator2(func):
    print("------>2 start")

    def wrapper(*args, **kwargs):
        func()
        print("----->铺地板")

    print("------>2 end")
    return wrapper

# 装饰器3
def decorator3(func):
    print("------>3 start")

    def wrapper(*args, **kwargs):
        func()
        print("----->装门")

    print("------>3 end")
    return wrapper

# 使用装饰器
@decorator3
@decorator2
@decorator1
def house():
    print("我是毛坯房")

print(house)
house()

输出:

------>1 start
------>1 end
------>2 start
------>2 end
------>3 start
------>3 end
<function decorator3.<locals>.wrapper at 0x0000021078735A60>
我是毛坯房
----->刷漆
----->铺地板
----->装门

装饰器也可以带参数:

  • 带参数的装饰器是在原装饰器的外面再套了一个函数,用于接收装饰器参数。
def outer(a): # 第一层:负责接收装饰器的参数
    def decorator(func): # 第二层:负责接收被装饰函数
        def wrapper(*args, **kwargs): # 第三层:负责接收被装饰函数的参数
            func(*args)
            print("----->铺地砖{}块".format(a))

        return wrapper

    return decorator

@outer(10)
def house(time):
    print("我{}拿到房子的要是,是毛坯房...".format(time))

@outer(100)
def street():
    print("新街道的名字:黑泉路")

print(house)
house("2022-12-25")

street()

输出:

<function outer.<locals>.decorator.<locals>.wrapper at 0x0000022C1C2358C8>
我2022-12-25拿到房子的要是,是毛坯房...
----->铺地砖10块
新街道的名字:黑泉路
----->铺地砖100块

6. 异常

在这里插入图片描述

6.1 常见异常类型

在这里插入图片描述

6.2 异常处理机制

6.2.1 try…except

在这里插入图片描述

6.2.2 try…except…else

在这里插入图片描述

6.2.3 try…except…else…finally

在这里插入图片描述

6.2.4 traceback模块

在这里插入图片描述

7. 类和对象

7.1 类的创建和对象的创建

类也是一个对象,称为类对象,在运行时加载到内存中,创建对象之后称为实例对象

类由以下四个部分组成:

  • 类属性:类中方法外的变量成为类属性,为该类的所有对象所共享。
  • 实例方法:由对象调用,方法的第一个参数默认是self构造方法析构方法也属于实例方法。
    • 普通方法
    • 魔术方法:__名字__()。和普通方法唯一的不同是,普通方法需要调用,而魔术方法是在特定时刻自动触发。
      • __init__():初始化魔术方法。初始化对象时触发(不是实例化触发,但是和实例化在一个操作中)。
      • __new__():实例化魔术方法。在实例化时触发。
      • __call__():调用对象的魔术方法。将对象当作函数调用时触发 对象()
      • __del__():垃圾回收之前的操作,在python中作用不大,因为Python有自带的垃圾回收机制。当一块空间没有了任何引用时触发。
      • __str__():打印对象名时触发。默认是打印对象的地址。
  • 类方法:使用@classmethod修饰的方法。使用类名和对象名访问,有一个cls参数,代表定义类方法的类,通过 cls 参数可以访问类属性。由于没有self参数,所以类方法是无法访问成员变量的。类方法中可以调用构造函数创建对象。
  • 静态方法:使用@staticmethod修饰的方法。使用类名和对象名访问的方法,没有self参数,所以无法访问类的成员变量也没有cls参数,所以也无法通过cls访问类变量,但是可以通过类名.属性/方法来访问类的属性和方法。

注意:创建对象时(比如 p = Person())的底层逻辑是:

  1. 查看内存中是否已经加载Person类
  2. 触发 __new__() 方法申请一段和Person类一样的内存空间,并返回地址
  3. 查找Person类中是否有__init__()方法。如果没有,则直接将地址赋给p;如果有,则将地址扔给该方法的self,并进行初始化,执行完__init__()后将地址赋给p。

在这里插入图片描述

class Student:
    native_place = "吉林" # 类属性
    # 构造方法
    def __init__(self,name,age):
        self.name = name # self.name称为实例属性
        self.age = age

    # 析构方法,在对象被销毁之前自动调用
    def __del__(self):
        print('===析构函数===')

    # 实例方法
    def eat(self):
        print("学生在吃饭")

    # 静态方法
    @staticmethod
    def method(): # 没有默认的self参数,所以静态方法不能使用对象的属性和方法
        print("我使用了静态方法")

    # 类方法
    @classmethod
    def cm(cls): # 没有
        print("我使用了类方法")

print(id(Student)) # 3005583791640
print(type(Student)) # <class 'type'>
print(Student) # <class '__main__.Student'>
print('-----------')

# 创建Student类的对象
stu1 = Student('张三',20)
print(id(stu1)) # 1301185945840
print(type(stu1)) # <class '__main__.Student'>
print(stu1) # <__main__.Student object at 0x0000012EF4B9E0F0>

stu1.eat()
print(stu1.name)
print(stu1.age)

print('-------------')
Student.eat(stu1) # 相当于把stu1这个对象引用传递给eat方法的self

# 类属性的使用方式
print(Student.native_place) # 吉林
stu2 = Student('李四',30)
print(stu1.native_place) # 吉林
print(stu2.native_place) # 吉林
Student.native_place = '天津'
print(stu1.native_place) # 天津
print(stu2.native_place) # 天津

print('----------类方法的使用方式----------')
Student.cm()
print('----------静态方法的使用方式----------')
Student.method()

# 主动删除两个对象,del之前也会自动调用__del__析构函数
del stu1
del stu2

再讲__del__()

  • 对象赋值,多个变量指向同一个对象的地址
  • 删除地址的引用 del 变量名
  • 查看地址的引用次数 sys.getrefcount(对象名)
  • 当一块空间没有了任何引用,默认执行__del__()
7.1.1 属性的获取机制

在这里插入图片描述

class Tool(object):
    # 定义类属性
    count = 0

    def __init__(self,name):
        self.name = name

        # 让类属性+1
        Tool.count += 1

tool1 = Tool('斧头')
tool2 = Tool('榔头')
tool3 = Tool('水桶')

print("工具对象总数 %d" % tool3.count)
print("===> %d" % Tool.count)
print("------------------------")
tool3.count = 99
print("工具对象总数 %d" % tool3.count)
print("===> %d" % Tool.count)

'''
输出:
工具对象总数 3
===> 3
------------------------
工具对象总数 99
===> 3
'''
7.1.2 单例设计模式
  • 目的:让类创建的对象,在系统中只有唯一的一个实例
  • 每一次执行类名()返回的对象,内存地址都是相同的

在这里插入图片描述

class MusicPlayer(object):
    # 记录第一个被创建对象的引用
    instance = None

    def __new__(cls, *args, **kwargs):
        if cls.instance is None:
            cls.instance = super().__new__(cls)
        return cls.instance

player1 = MusicPlayer()
print(player1)
player2 = MusicPlayer()
print(player2)

"""
输出:
<__main__.MusicPlayer object at 0x000002BEF38999B0>
<__main__.MusicPlayer object at 0x000002BEF38999B0>
"""

7.2 动态绑定属性和方法

在这里插入图片描述

class Student:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def eat(self):
        print(self.name + '在吃饭')

stu1 = Student('张三', 30)
stu2 = Student('李四', 40)

stu2.gender = '男'
print(stu1.name, stu1.age) # 张三 30
print(stu2.name, stu2.age, stu2.gender) # 李四 40 男


def show():
    print('定义在类之外,函数')

stu1.show = show
stu1.show() # 定义在类之外,函数

有一个写的挺好的博客详细介绍了动态添加属性与方法

8. 面向对象的三大特征

在这里插入图片描述

  • 封装:提高程序的安全性。
    • 在Python中没有专门的权限修饰符,如果该属性不希望在类外部被访问,前边使用两个"_"
  • 继承:提高程序的复用性
  • 多态:提高程序的可扩展性和可维护性

8.1 封装

class Student:
    def __init__(self,name,age):
        self.name = name
        self.__age = age # 私有化

    def show(self):
        print(self.name,self.__age)

stu = Student('张三',20)
stu.show() # 张三 20

print(stu.name) # 张三
# print(stu.__age) # AttributeError: 'Student' object has no attribute '__age'
print(dir(stu)) # dir([object]) 返回模块的属性列表,stu的属性列表包含'_Student__age'
print(stu._Student__age) # 20 在类的外部也可以访问,但不建议访问
8.1.1 @property装饰器

作用是将私有的属性变成无私有一样使用。

例子请看私有化之property装饰器方式

8.2 继承

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class Student:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def __str__(self):
        return '我的名字是{0},今年{1}岁了'.format(self.name,self.age)

stu = Student('张三',20)
print(dir(stu))
# __str__()方法用于对stu对象的描述,重写之前是输出stu的地址
print(stu) # 我的名字是张三,今年20岁了
8.2.1 父类的私有属性和私有方法
  • 子类对象 不能 在自己的方法内部,直接 访问 父类的 私有属性私有方法
  • 子类对象 可以通过 父类公有方法 间接 访问到 私有属性私有方法
    • 私有属性、方法 是对象的隐私,不对外公开,外界 以及 子类 都不能直接访问
    • 私有属性、方法 通常用于做一些内部的事情

在这里插入图片描述

8.2.2 多继承

问题1:如果 不同的父类 中存在 同名的方法子类对象 在调用方法时,会调用 哪一个父类中的方法呢?

Python中的MRO——方法搜索顺序

  • Python中针对类提供了一个内置属性__mro__可以查看方法搜索顺序
  • MRO时method resulution order,主要用于在多继承时判断方法、属性的调用路径
    • 在搜索方法时,是按照__mro__的输出结果从左至右的顺序查找的
    • 如果在当前类中找到方法,就直接执行,不再搜索
    • 如果没有找到,就查找下一个类中是否右对应的方法,如果找到,就直接执行,不再搜索
    • 如果找到最后一个类,还没有找到方法,程序报错
class A(object):
	def test(self):
		print('A----test方法')
	def demo(self):
		print('A----demo方法')

class B(object):
	def test(self):
		print('B----test方法')
	def demo(self):
		print('B----demo方法')

class C(B,A):
	pass

print(C.__mro__)
c = C()
c.test()
c.demo()

"""
输出:
(<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
B----test方法
B----demo方法
"""

8.3 多态

在这里插入图片描述

8.4 特殊属性和特殊方法

在这里插入图片描述

a = 20
b = 100
c = a + b # 两个整数类型的对象的相加操作
d = a.__add__(b) # 底层调用了__add__方法

print(c)  # 120
print(d)  # 120

# 自定义对象的相加
class Student:
    def __init__(self, name):
        self.name = name
    # 重写__add__方法,实现自定义对象的相加
    def __add__(self, other):
        return self.name + other.name
    # 重写len方法,返回对象的长度
    def __len__(self):
        return len(self.name)

stu1 = Student('张三')
stu2 = Student('李四')
s = stu1 + stu2
print(s) # 张三李四
s = stu1.__add__(stu2)
print(s) # 张三李四
print('---------------')

lst = [11,22,33,44]
print(len(lst)) # 4
print(lst.__len__()) # 4 底层调用
print(len(stu1)) # 2
class Person(object):
    def __new__(cls, *args, **kwargs):
        print('__new__被调用执行了,cls的id值为:{0}'.format(id(cls)))
        obj = super().__new__(cls)
        print('创建的对象的id为:{0}'.format(id(obj)))
        return obj

    def __init__(self, name, age):
        print('__init__被调用了,self的id值为:{0}'.format(id(self)))
        self.name = name
        self.age = age

print('object这个类对象的id为:{0}'.format(id(object))) # object这个类对象的id为:1874967952
print('Person这个类对象的id为:{0}'.format(id(Person))) # Person这个类对象的id为:2561543033320

# 创建Person类的实例对象
p1 = Person('张三',20)
print('p1这个Person类的实例对象的id为:{0}'.format(id(p1)))
# __new__被调用执行了,cls的id值位2561543033320
# 创建的对象的id位:2561549439440
# __init__被调用了,self的id值为:2561549439440
# p1这个Person类的实例对象的id为:2561549439440

在这里插入图片描述

8.5 类的赋值和浅拷贝

在这里插入图片描述

class CPU:
    pass
class Disk:
    pass
class Computer:
    def __init__(self,cpu,disk):
        self.cpu=cpu
        self.disk=disk
#变量的赋值,同一个实例对象CPU()赋值给两个变量cpu1、cpu2
cpu1=CPU()
cpu2=cpu1
print(cpu1,id(cpu1)) # 5144
print(cpu2,id(cpu2)) # 5144
#类有浅拷贝
print('--------------')
disk=Disk()
computer=Computer(cpu1,disk)
#浅拷贝
import copy
computer2=copy.copy(computer)
print(computer,computer.cpu,computer.disk) # E048 8DD8 E0B8
print(computer2,computer2.cpu,computer2.disk) # E0F0 8DD8 E0B8
print('------------------------------')
#深拷贝
computer3=copy.deepcopy(computer)
print(computer,computer.cpu,computer.disk) # E048 8DD8 E0B8
print(computer3,computer3.cpu,computer3.disk) # E128 E3C8 E9B0

9. 模块

模块就是.py文件。同级目录下 不同py文件之间的调用就是模块调用。

9.1 导入模块

# 方法1:导入模块,当引用模块内的函数/变量/类时,用moduleName.函数/变量/类名
import moduleName [as anotherName]
# 方法2:导入模块中的函数/变量/类,直接通过 函数/变量/类名 使用
from moduleName import 函数/变量/类
# 方法3:导入模块中的所有 可用的 函数/变量/类,直接通过 函数/变量/类名 使用
# 上面 可用的 意思是module中__all__列表包含的函数/变量/类,如果没有写__all__列表,则代表所有函数/变量/类都是可用的。
from muduleName import *

# 注意:__all__只是针对 * 起作用的,即使__all__中没有包含A函数,但from moduleName import A 依然能够将A导入

9.2 __name__属性

当我们导入模块时,如果该模块中执行了一段代码,那么在导入该模块的时候也会执行这一段代码。这个是我们不想看到的,而__name__属性可以帮助我们避免这个问题。

__name__是一个记录模块名称的变量,程序检查该变量,以确定它在哪个模块中运行。如果这个被导入其他模块,在其他模块中不会执行这个模块的主程序。

main.py模块:

def say_hello():
    print("hello")

# 如果直接执行模块,__main__
if __name__ == "__main__":
    print(__name__)

    # 文件被导入时,能够直接执行的代码不需要被执行
    print("小明开发的模块")
    say_hello()
    
"""
输出:
__main__
小明开发的模块
hello

"""

test_import.py模块:

import main

print("-" * 50)

"""
输出:
--------------------------------------------------
"""

10. 包

包下可以包含多个模块,包中有一个__init__.py文件,当导入包的时候,会自动执行包下的__init__.py文件。

不同的包下可以包含相同的模块名,不会产生命名冲突。

下面以几种项目结构进行清晰的讲解:

项目结构一:

项目
    article
        |--models.py
        |--__init__.py
        |--...
    
    user
        |--models.py
        |--__init__.py
        |--...
    
    package.py

当项目结构如上所示时,如果在package.py中要调用同级包(article和user)中的模块中的类/函数/变量时,可以分为间接调用和直接调用:

# 模块.类/函数/变量 间接调用
from 包 import 模块
# 模块.类/函数/变量 间接调用
from 包 import * # 注意这种*的导入方法必须在包的__init__中用__all__中包含要导入的模块,否则无法导入。另外要区分好 __init__中的__all__ 和 模块中的 __all__
# 类/函数/变量 直接调用
from 包.模块 import 类/函数/变量
# 类/函数/变量 直接调用
from 包.模块 import *

项目结构二:

项目
    article
        |--models.py
        |--__init__.py
        |--...
    
    user
        |--models.py
        |--__init__.py
        |--test.py
        |--...
    
    package.py

当项目结构如上所示时,如果user包中的 test模块 要调用 user包中的 models模块时:

# 方法1:绝对路径
from user.models import 类/函数/变量
from user.models import *
import user.models as models

# 方法2:相对路径,注意models前面不能加. ,因为.会被默认翻译成__main__. ,但因为不存在__main__.models模块,所以会报错
from models import 类/函数/变量
from models import *
import models as models

如果 user包中的 test模块 要调用 package 模块中的 类/函数/变量 时:

import package
from package import 类/函数/变量
from package import *

10.1 __init__文件

当导入包的时候,会自动执行包下的__init__.py文件。

__init__.py文件的两个作用:

  • __init__.py文件中可以声明类/函数/变量,当导入包的时候,__init__.py文件中的类/函数/变量也会跟着导入,通过 包.类/函数/变量 使用。
  • 结合__all__=[通过*可以访问的模块],如果没有写__all__,那么通过from 包 import *将无法访问到包中的任何模块;如果写了__all__,那么通过from 包 import *将可以访问到__all__中包含的模块。明显, __init__中的__all__ 跟 模块中的__all__ 的作用是不一样的,有点相反的意思。

注意:导入模块和导入包的共同点:所有的from都是基于项目下进行的,比如说 from 模块 import * 的模块是项目下的模块

11. 文件读写

请看博主的另一篇文章Python常用内置模块使用

12. eval()函数

在这里插入图片描述
在这里插入图片描述

13. 列表推导式

格式:

[表达式 for 变量 in 旧列表] ---> 新列表
[表达式 for 变量 in 旧列表 if 条件] ---> 新列表
[表达式A if 条件 else 表达式B for 变量 in 旧列表] ---> 新列表

除了列表推导式,还有集合推导式和字典推导式。

集合推导式就是在列表推导式的基础上自动去重。

# 集合推导式
{表达式 for 变量 in 旧集合} ---> 新集合
{表达式 for 变量 in 旧集合 if 条件} ---> 新集合
{表达式A if 条件 else 表达式B for 变量 in 旧集合} ---> 新集合

# 字典推导式
{键表达式:值表达式 for key,value in 旧字典} ---> 新字典
{键表达式:值表达式 for key,value in 旧字典 if 条件} ---> 新字典
{键表达式A:值表达式A if 条件 else 键表达式B:值表达式B for 变量 in 旧字典} ---> 新字典

14. 生成器

  • 作用:取代列表推导式,避免一下生成全部元素,节省内存
  • 生成器的关键字是 generator
  • 定义生成器的方式:
    • 通过列表推导式方式
    • 函数 + yield
  • 产生元素:
    • next(generator)内置方法:每次调用都会产生一个新的元素,如果元素产生完毕,再次调用的话就会产生异常
    • 生成器方法:
      • __next__()方法
      • send(value)方法
# 列表推导式
# 直接创建列表,其中可能用不到全部元素,导致内存浪费
newlist = [x*3 for x in range(5)]
print(type(newlist))
print(newlist)

# 生成器是一个个的生成元素,这样不会造成内存浪费
# 得到生成器的方式一:小括号
g = (x*3 for x in range(5))
print(type(g))
print(g)

# 得到生成器元素
# 方式一:通过调用生成器的__next__()方法得到元素
print(g.__next__())
print(g.__next__())
# 方式二:next(), 内置函数得到生成器的元素
print(next(g))
print(next(g))
# 注意:如果取的元素次数超过生成器的总元素个数,报错

while True:
    try:
        e = next(g)
        print(e)
    except:
        print('没有更多元素了!')
        break

# 得到生成器的方式二:借助函数完成
# 只要函数中出现了yield关键字,函数就变成生成器了
def func():
    n = 0
    while True:
        n += 1
        yield n # 相当于 return n + 暂停

g = func() # 这里不是调用函数,而是产生一个生成器对象
print(g)

print(next(g))
print(next(g))

"""
输出:
<class 'list'>
[0, 3, 6, 9, 12]
<class 'generator'>
<generator object <genexpr> at 0x000001169FFCDF68>
0
3
6
9
12
没有更多元素了!
<generator object func at 0x00000116A0088360>
1
2
"""
"""
生成器方法:
    __next__():获取下一个元素
    send(value):向每次生成器调用中传值。第一次调用必须传递None

"""

def gen():
    i = 0
    while i < 5:
        temp = yield i # return 0 + 暂停 temp = '呵呵'
        print('temp:',temp)
        i += 1
    return '没有更多的数据!'

g = gen()

print(g.send(None))
n1 = g.send('呵呵')
print("n1:",n1)
n2 = g.send('哈哈')
print("n2:",n2)

"""
输出:
0
temp: 呵呵
n1: 1
temp: 哈哈
n2: 2
"""
# 进程 > 线程 > 协程
# 迅雷

def task1(n):
    for i in range(n):
        print("正在搬第{}块砖!".format(i))
        yield None

def task2(n):
    for i in range(n):
        print("正在听第{}首歌!".format(i))
        yield None

# 只能顺序执行,先搬完砖,再听歌
# task1(10)
# task2(5)

# 任务:搬一块砖,听一首歌
g1 = task1(10)
g2 = task2(5)
i = 0
while i < 5:
    try:
        g1.__next__()
        g2.__next__()
    except:
        i += 1

"""
输出:
正在搬第0块砖!
正在听第0首歌!
正在搬第1块砖!
正在听第1首歌!
正在搬第2块砖!
正在听第2首歌!
正在搬第3块砖!
正在听第3首歌!
正在搬第4块砖!
正在听第4首歌!
正在搬第5块砖!
正在搬第6块砖!
正在搬第7块砖!
正在搬第8块砖!
正在搬第9块砖!
"""

15. 可迭代对象

问题1:什么是迭代器?

  • 可以被next()函数调用并不断返回下一个值的对象称为迭代器Iterable
  • 迭代是访问集合元素(这里的集合指的是list、dict、set等集合)的一种方式。迭代器是一个可以记住遍历位置的对象。
  • 迭代器对象从集合的第一个元素开始访问,直到所有的元素访问完结束。
  • 迭代器只能往前不能后退。

问题2:可迭代对象包括什么?

  • 生成器
  • 元组
  • 列表
  • 集合
  • 字典
  • 字符串

问题3:如何判断一个对象是否可迭代?

  • 利用isinstance(x, A_tuple)来判断
from collections import Iterable

list1 = [1,4,7,8,8]
f = isinstance(list1,Iterable)
print(f)

f = isinstance('abc',Iterable)
print(f)

f = isinstance(100,Iterable)
print(f)

g = (x+1 for x in range(10))
f = isinstance(g,Iterable)
print(f)

"""
输出:
True
True
False
True
"""

问题4:可迭代的 是不是肯定是迭代器

  • 生成器是可迭代的,也是迭代器
  • 列表、字典、元组、集合、字符串是可迭代的,但不是迭代器,可以通过iter()函数将可迭代的变成一个迭代器
list1 = [1,2,3,4,5]
# print(next(list1)) # TypeError: 'list' object is not an iterator

list1 = iter(list1)
print(next(list1)) # 1

16. 其他

16.1 Shebang Line

#!/usr/bin/python3

添加Shebang line的目的是定义解释器的位置,然后我们就可以在 Unix 系统上运行 file.py 并自动理解这是一个 python 脚本。此时我们运行python脚本的时候不用就可以省略python关键字了。

比如file.py如下所示:

#!/usr/bin/python3
 
print("Hello shebang line")

然后我们在Unix上通过命令行运行:

./file.py

或者,我们可以不加shebang line,然后我们可以添加python关键字来执行程序:

python ./file.py

Windows系统会自动忽略shebang line

16.2 编码格式

# -*- coding: utf-8 -*-

一般在程序的开头会看到上面这句,它是为了告诉Python解释器,按照UTF-8编码读取源代码,否则,你在源代码中写的中文输出可能会有乱码。

申明了UTF-8编码并不意味着你的.py文件就是UTF-8编码的,必须并且要确保文本编辑器正在使用UTF-8 without BOM编码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值