文章目录
一、函数基本语法
二、高阶函数
三、装饰器
四、包和模块
五、面向对象
Python 函数是组织好的、可重复使用的、用来实现单一或相关联功能的代码块。它们提高了代码的可读性和可维护性,使得代码更加模块化。
一、函数基本语法
1.1 函数概述
在一个完整的项目中,某些功能可能会被反复使用,如果将反复出现的代码封装成函数,以后如果要继续使用该功能则直接使用函数即可,另外,如果要修改需求,只需要修改函数即可。
本质:对某些特殊功能的封装,使用函数的三大优点:代码重用、保持一致性、可扩展性。
1.2 函数定义和调用
使用def关键字来定义一个函数,函数名后面跟着圆括号,圆括号中可以包含参数,然后是冒号:,最后是函数体(缩进的代码块)。
语法:
def 函数名(变量1,变量2....):
函数体
return 返回值
函数定义四种方式(无参无返回值、有参无返回值、无参有返回值、有参有返回值)
print('start')
# 1.无参无返回值
def func1():
print('ok~~~11111')
# 2.有参无返回值
def func2(a,b):
print('ok~~~~~2222',a,b)
# 3.无参有返回值
def func3():
print('ok~~~33333')
return 'abc'
# 4.有参有返回值
def func4(num1,num2,num3):
print('ok~~~~~4444',num1,num2,num3)
return num1 + num2 + num3
print('end')
函数调用语法:通过 函数名() 即可完成调用
每次函数调用时,函数都会从头开始执行,当这个函数中的代码执行完毕后,意味着调用结束了,如果函数中执行到了return也会结束函数。
def f1():
f2()
print('1111')
def f2():
print('222222')
f3()
def f3():
print('333333')
1.3 函数参数
在Python函数中,参数可以分为多种类型,包括必须参数、默认参数、关键字参数和不定长参数:
参数类型 | 描述 | 示例 |
必须参数 | 函数调用时参数的顺序必须和定义时一致 | def func(a, b): return a + b |
默认参数 | 如果没有传递参数,则使用定义函数时指定的默认值 | def func(a, b=2): return a + b |
关键字参数 | 允许按照参数名传递参数,不需要考虑参数的顺序 | def func(a, b): return a + b 调用时:func(b=3, a=1) |
不定长参数(*args) | 参数在函数内部作为元组处理 | def func(*args): return sum(args) |
不定长参数(**kwargs) | 用于传递一个不定长度的关键字参数字典给函数 | def func(**kwargs): for key, value in kwargs.items(): print(key, value) |
值传递:传参的时候,传递的是不可变的数据类型,如:int/float/str/tuple/bool,当形参发生修改,对实参没有影响。
引用传递:传参的时候,传递的是可变的数据类型,如:list/dict/set等,当形参中的元素发生修改,则实参会随着修改。
1.4 返回值
返回值:表示函数的运算结果,在哪里调用函数,返回值就返回到哪里,函数返回值语法结构如下:
def 函数名(形参):
函数体【某个功能】
return 返回值
Python中return、break、exit()区别如下:
分类 | 用途 | 说明 | 终止级别 |
return | 从函数返回一个值给调用者 | 可以是任何类型(包括None) | 函数 |
break | 跳出当前循环(如for、while) | None(不返回值,但结束循环) | 循环 |
exit() | 退出Python程序 | 表示退出状态码,默认为0 | 程序 |
1.5 匿名函数
匿名函数为不再使用def这种标准形式定义函数,而是使用lambda表达式来创建函数,该函数没有函数名,被称为匿名函数。
匿名函数语法:lambda 形参列表:返回值
lambda函数的特点:
1简洁性:对于简单的函数,使用lambda可以使代码更加简洁。
2匿名性:lambda函数没有名称。
3函数对象:lambda表达式会生成一个函数对象,可以将其赋值给一个变量,然后通过这个变量来调用它。
# 标准函数的定义
def f1(n):
print('函数被调用了~~~')
return n + 1
print(f1) # <function f1 at 0x00000228F1D7F040>
r1 = f1(6) # 函数的调用
print(r1) # 函数的返回值
# 匿名函数/lambda表达式,语法:lambda 形参列表:返回值
f2 = lambda n: n + 1
print(f2) # <function <lambda> at 0x000002860924C3A0>
r2 = f2(8) # 调用函数
print(r2) # 函数的返回值
1.6 函数嵌套
def func1():
def func2():
xxx
# func1:外部函数
# func2:内部函数
# func1和func2的参数,返回值的使用和最基本的使用完全相同
# 2.嵌套函数的调用
# 方式一:在外部函数中直接调用内部函数
def func1():
print('外部~~~~11111')
n1 = 10
def func2():
n2 = 20
print(n1 + n2)
print('内部~~~111111')
func2() # 调用内部函数func2
print('外部~~~~222222')
func1()
# 方式二:将内部函数作为外部函数的返回值返回
def func1():
print('外部~~~~11111')
n1 = 10
def func2():
n2 = 20
print(n1 + n2)
print('内部~~~111111')
print('外部~~~~222222')
1.7 函数闭包
闭包的概念:如果两个函数嵌套定义,如果在内部函数中访问了外部函数中的变量,则构成一个闭包。
闭包的用法分类:内外部函数均无参、外部函数有参、内外部函数有参、内外部函数有参且内部函数有返回值。
# 闭包:如果两个函数嵌套定义,如果在内部函数中访问了外部函数中的变量,则构成一个闭包 *********
# 闭包的常见写法
# a.内外部函数均无参
def func1():
n1 = 10
def func2():
n2 = 20
print(n1 + n2)
return func2
f = func1()
print('外部函数调用完毕') # 当外部函数被调用完毕之后,按理n1会被销毁
f() # 但是,在函数的嵌套定义中,由于内部函数访问了外部函数中的变量,所以当外部函数调用完毕之后,内部函数仍然可以访问到外部函数中的变量
# b.外部函数有参
# a,b和n1都属于外部函数中的变量,只要这三者中的任何一个被func2访问,则都会构成闭包
def func1(a,b):
n1 = 10
def func2():
n2 = 20
print(n1 + n2,a,b)
return func2
f = func1(3,2)
f()
# c.内外部函数有参
def func1(a,b):
n1 = 10
def func2(num1,num2,num3):
n2 = 20
print(n1 + n2,a,b,num1,num2,num3)
return func2
f = func1(3,2)
f(55,66,77) # 注意:相当于调用的是func2,所以一定要注意和func2的参数保持匹配
# d.内外部函数有参,内部函数有返回值
def func1(a,b):
n1 = 10
def func2(num1,num2,num3):
n2 = 20
print(n1 + n2,a,b,num1,num2,num3)
return n1 + n2
return func2
f = func1(3,2)
r = f(55,66,77)
print(r)
# 注意:虽然是函数嵌套定义,虽然是闭包,但是内外部函数本质上和普通函数的用发完全相同,默认参数,关键字参数,不定长参数和返回值都一样使用
1.8 变量作用域
变量的作用域指的是变量可以使用的范围,程序的变量并不是在任意位置都可以访问,访问权限取决于这个变量是在哪里定义的,变量的作用域决定了在哪一部分程序可以访问哪个特定的变量名称。
函数变量作用域分类介绍如下:
作用域类型 | 描述 | 生命周期 |
局部作用域(Local ) | 在函数内部定义的变量,只能在函数内部访问 | 函数调用时创建,函数结束时销毁 |
函数作用域(Enclosing ) | 在嵌套函数(内部函数)中访问其外部函数中的变量 | 外部函数调用时创建,外部函数结束时销毁 |
全局作用域(Global) | 可以在整个模块中访问,包括在函数内部 | 程序开始时创建,程序结束时销毁 |
内置作用域(Built-in) | Python内置的变量和函数,如len(),max()等 | 解释器启动时创建,解释器关闭时销毁 |
1.9 生成器和迭代器
(1)生成器
生成器概念:在Python中,一边使用和一边计算的机制被称为生成器(generator),在代码执行的过程中,大量的内存空间会被节约下来。
生成器定义方式:
a.将列表推导式中的[]改为()
num = (n ** 2 for n in range(10))
print(type(num)) # <class 'generator'>
b.函数结合yield,定义函数生成器
def func1():
yield 10
num = func1()
print(type(num)) #<class 'generator'>
(2)迭代器
可迭代对象(Iterable)可以直接作用于for循环的对象,如:list、tuple、dict、set、str、range()、生成器等;迭代器(Iterator)可以直接作用于for循环且可以通过next()获取下一个元素的对象,如生成器。
迭代器一定是可迭代对象,但是可迭代对象不一定是迭代器,可以通过系统功能iter()将不是迭代器的可迭代对象转换为迭代器。
from collections.abc import Iterable,Iterator
print(isinstance(iter([1,2,5]),Iterator)) # 结果为True
print(isinstance(iter((5,6,7)),Iterator)) # 结果为True
print(isinstance(iter('qwer'),Iterator)) # 结果为True
二、高阶函数
高阶函数是指是指那些可以接收函数作为参数,或者返回一个函数作为结果的函数。常用的高阶函数为map()、reduce()、filter()、sorted()。
(1)map()高阶函数
map()函数将一个函数应用于一个或多个序列的每一个元素,并返回一个迭代器。
语法结构:map(func,iterable)
功能:将iterable中的每一个元素自动传参给func函数,func会返回一个值,该值会称为iterator中的元素。
(2)reduce()高阶函数
reduce()函数会对参数序列中元素进行累积。
语法结构:reduce(func,sequence), func函数必须设置两个参数,设置1个返回值。
功能:将seq中的第0个和第1个元素传递给func…直到seq中的所有元素全部参与运算才会停止,最后得到一个结果。
(3)filter()高阶函数
filter()函数用于过滤序列,过滤掉不符合条件的元素,返回一个迭代器。
语法结构:filter(func,iterable),返回值是一个iterator
功能:将iterable中的元素依次传递给func函数,如果func的返回值为True,则表示需要保留当前元素,如果func的返回值为False,则表示当前元素需要过滤掉。
(4)sorted()高阶函数
sorted()函数可以对可迭代对象进行排序。
语法结构:sorted(iterable,reverse,key=func),生成1个新的可迭代对象
功能:key参数允许你指定一个函数,该函数会在每个元素上调用,并使用其返回值进行排序。
三、装饰器
3.1 装饰器概念
在不修改原函数的基础上增加新的功能,这种在代码运行期间动态执行的机制被称为装饰器(Decorator),装饰器的作用为已经存在的函数或者类添加额外的功能,本质是内部函数访问外部函数中的变量。
3.2 装饰器语法
使用@xxx可以将一个装饰器作用于一个函数上,只需要将@xxx书写在一个函数的前面,则表示xxx装饰器装饰指定的函数。
def outter(func):
print('外部函数~~~~~~start')
def inner():
# 调用原函数
print('inner~~~~~~~~~~')
func()
# 增加新的功能
print('new~~~~~~')
print('外部函数~~~~~~end')
return inner
@outter # 等价于f = outter(a)。调用了外部函数
def a():
print('春节快乐')
print(a) # <function outter.<locals>.inner at 0x000001AE54DCC430>
a() # 等价于 f(),调用了内部函数
总结:
a.使用@xxx装饰某个函数,则该装饰器一定要先存在,然后才能使用
b.@xxx本质上表示调用装饰器的外部函数,自动将已知的函数传参给了func,同时自动将返回值inner接出来
c.当原函数传参给func之后,此时原函数的函数名就会给重新赋值,赋值为inner,所以a()表示调用的是inner()
四、包和模块
4.1 包和模块概念
模块Module:以.py为后缀的文件,称之为模块。
包Package:即文件夹,传统包里默认有一个__init__.py文件,可以为空文件,它是包的标志性文件,在需要情况下可以在里面进行一些包的初始化工作。
4.2 模块使用语法
方式一:import 模块名
方式二:from 模块名 import 变量名/函数名/类名
通过from xxx import xxx的方式导入,访问变量或调用函数的时候,可以直接书写变量名或函数名,当不同模块中出现了重名的变量或者函数,此时相互之间会影响,只能访问到后导入的内容。
4.3 默认模块
模块名 | 功能描述 | 主要类或函数 |
time | 时间相关功能模块 | time():返回当前时间的时间戳 |
localtime():将时间戳转换为本地时间 | ||
gmtime():将时间戳转换为UTC时间 | ||
strftime():将struct_time对象格式化为字符串 | ||
strptime():将字符串解析为struct_time对象 | ||
sleep():使程序暂停指定的秒数 | ||
datetime | 日期和时间的相关功能 | now():获取当前时间 |
datetime(year, month, day):创建指定日期时间的对象 | ||
os | 文件目录操作管理功能 | os.listdir():列出目录下的所有文件和子目录 |
os.mkdir():创建新目录 | ||
os.rmdir():删除空目录 | ||
os.rename():重命名文件或目录 | ||
os.remove():删除文件 | ||
os.path.exists():检查路径是否存在。 | ||
os.path.isfile():检查路径是否是文件。 | ||
os.path.isdir():检查路径是否是目录。 |
五、面向对象
5.1 面向对象概述
面向对象是Python中的一种重要编程方式,它基于“对象”的概念来编写代码,使代码更加模块化、易于维护和易于扩展,有三大特征:封装、继承、多态。
5.2 类和对象概念
类:是一个具有特殊功能的实体的集合或群体,用于创建具有相同属性和方法的对象。
对象:在一个类中,一个具有特殊功能的实体,能够帮忙解决特定的问题,对象是类的实例。通过类创建对象时,会调用类的构造函数(__init__方法)来初始化对象的属性。
类和对象两者之间的关系:类用于描述某一类对象的共同特征,而对象则是类的具体存在。
5.3 类和对象定义
类的语法结构:
class 类名():
类体
说明:(1)在同一个py文件中,可以定义多个类,但是如果要实现的需求较为复杂,一般会结合模块使用,在一个模块中定义一个类。
(2)定义类的过程中,类名后面的()可以省略,创建对象时的()不能省略。
(3)同一个类,默认情况下,可以创建无数个对象,每个对象都会被分配不同的地址。
(4)直接输出对象,默认的情况下,会得到一个地址。
5.4 对象创建
m1 = MyClass1()
print(m1)
m2 = MyClass2()
print(m2,id(m2))
m22 = MyClass2()
print(m22,id(m22))
5.5 构造函数
构造函数是指创建对象时本身所运行的函数,Python使用__init__()函数作为对象的构造方法,当用户要在对象内指向对象本身时,可以使用self关键字,self代表对象本身。
class Person():
def __init__(self,name,age):
self.name = name
self.age = age
def show(self):
print(f'name:{self.name},age:{self.age}')
5.6 对象属性的动态绑定和限制绑定
(1)对象属性的动态绑定
只要是 对象.属性 = 值 类似这样的语法,都是给对象动态绑定属性,在默认情况下,对于属性的绑定没有任何限制。
(2)限制绑定
用__slots__限制对象属性的动态绑定,定义一个元组,将属性名以字符串的形式书写在元组中。
语法结构:__slots__ = ('xx1','xx2','xx3')
class Doctor():
# 用__slots__限制对象属性的动态绑定,定义一个元组,将属性名以字符串的形式书写在元组中,一般是结合实际需求或实际情况确定
__slots__ = ('name','age','kind')
# 注意:当元组中只有一个元素的时候,要添加逗号
# __slots__ = ('name',)
def __init__(self,name,age):
self.name = name
self.age = age
doc = Doctor('张大夫',40)
doc.kind = '外科'
print(doc.name,doc.age,doc.kind)
# doc.a = 234 # AttributeError: 'Doctor' object has no attribute 'a'
# doc.eggw= 45
5.7 类中的属性
(1)定义位置不同
类属性:定义在类中
实例属性:动态绑定的属性(在__init__中或在类的外面直接动态绑定定义)
class MyClass():
hobby = 'study' #类属性
def __init__(self,name):
self.name = name #实例属性
num1.age = 18 #实例属性
num = MyClass
(2)访问方式不同
类属性可以通过类名或对象访问
print(MyClass.hobby)
print(num.hobby)
实例属性只能通过对象访问
print(num.name)
(3)访问优先级不同
当类属性和实例属性重名时,通过对象访问,实例属性优先级最高。
(4)内存中的类属性和实例属性
类属性共享同一份地址,而实例属性为不同的地址。
(5)使用场景不同
类属性用于表示多个对象共享的数据,实例属性表示每个对象特有的数据。
5.8 类中的函数
类中的函数主要分为:实例函数、类函数、静态函数。
(1)实例函数
使用def关键字来定义一个方法,与一般函数定义不同,实例函数必须包self,self代表类的实例,语法结构如下:
def 实例函数名(self,参数)
函数体
return 返回值
语法说明:实例函数必须创建在类中,函数的第一个参数是self,代表对象本身, “return 返回值”可有可无,需要返回则用。
(2)类函数
类函数用@classmethod装饰器装饰,第一个形参是cls,cls是class的缩写,表示当前类,类函数之间相互调用,格式:cls.xxx()。语法结构如下:
@classmethod
def 函数名(cls):
函数体
return 返回值
(3)静态函数
静态函数用@staticmethod装饰器装饰,没有self函数,导致其无法访问类的实例属性;没有cls参数,导致无法访问类属性。
@staticmethod
def 函数名(cls):
函数体
return 返回值
实例函数、类函数、静态函数调用区别:实例函数只能通过对象调用,静态函数和类函数可以通过类名或对象调用。
5.9 面向对象的三大特征
面向对象的特征:封装、继承、多态。
(1)封装
封装的本质:将类中的属性进行私有化,私有属性表示只能在当前类中被直接访问。
属性私有化示例:
class Person():
__slots__ = ("__name", "__age") #限制动态绑定
def __init__(self, name, age):
# 私有属性,只需要在属性名的前面添加两个下划线__
self.__name = name
self.__age = age
def show(self):
print(f"姓名:{self.__name},年龄:{self.__age}")
# 创建对象,通过对象可以直接访问
p1 = Person("张三", 10)
# 通过对象无法直接访问
print(p1.__name,p1.__age) #报错:AttributeError: 'Person' object has no attribute 'name'
p1.show() #输出 姓名:张三,年龄:10
# 获取值
def func1(self):
return self.__name
print(p1.func1()) #输出 张三
# 修改值
def func2(self,a):
self.__name = a
p1.func2('李四')
p1.show() #输出 姓名:李四,年龄:10
(2)继承
继承允许一个类(子类)继承另一个类(父类)的属性和方法,从而实现代码的重用,一个子类可以继承多个父类。语法结构如下:
class 子类类名(父类类名):
类体
class 子类类名(父类类名1,父类类名2.......):
类体
继承示例:
# 父类
class Person(): # 在此处的()中什么都不写,默认的父类仍然是object
def __init__(self,name,age):
self.name = name
self.age = age
def show(self):
print('showing')
# 子类
class Doctor(Person):
def __init__(self,name,age,kind):
# 在子类的__init__中调用父类的__init__
# 方式一:super(当前类,self).__init__(参数列表)
# 方式二:super().__init__(参数列表)
# 方式三:父类类名.__init__(self,参数列表)
Person.__init__(self,name,age)
d = Doctor('王医生',22,'其他')
print(d.name,d.age) # 输出 王医生 45
(3)多态
继承是多态的前提,体现方式分为:一种事物的多种体现形式,定义时并不确定是什么类型,要调用的是哪个方法,只有运行的时候才能确定调用的是哪个。
class Animal():
def style(self):
print('walking')
class Cat(Animal):
pass
class Dog(Animal):
pass
class Pig(Animal):
pass
class Bird(Animal):
def style(self):
print('flying')
class Fish(Animal):
def style(self):
print('swimming')
5.10 函数重写
重写(override):在继承的前提下,在子类中重新实现了父类中的函数功能。
重写函数注意事项:
1输出对象时会调用魔术函数__str__,该函数返回当前对象在计算机中的地址;
2当重写__str__时,返回值必须是一个字符串,一般是和当前对象相关的属性信息;
3当子类中重写了父类中的函数,创建子类对象调用函数,优先调用的是子类中的函数。
class Person(object):
def __init__(self,name,age):
self.name = name
self.age = age
def show(self):
print(f'name:{self.name},age:{self.age}')
# 注意2:当重写__str__时,返回值必须是一个字符串,表示一个对象的字符串描述信息,一般是和当前对象相关的属性信息
def __str__(self):
# TypeError: __str__ returned non-string (type NoneType)
return f'name:{self.name},age:{self.age}'
5.11 运算符重载
运算符重载是指在类中定义特殊方法,使得该类的实例对象可以像内置类型一样进行运算,重写是指函数存在但是实现不同的需求,重载是指不支持指定的运算,通过重载让支持运算。
class Person():
def __init__(self,age):
self.age = age
def __gt__(self, other):
return self.age > other.age
p1 = Person(10)
p2 = Person(23)
print(p1 > p2)
print(p1.__gt__(p2))
print(Person.__dict__)
5.12 对象的内置内容
内置对象:
__slots__:限制对象属性的动态绑定
__dict__:获取类或对象的所有信息【属性和函数】,返回一个字典
__module__;获取指定对象属于哪个模块,如果时当前模块,则结果为__main__,如果是其他模块,则结果为模块名
__name__:如果结果为__main__则说明运行的是当前文件,如果是模块名则表示运行的是其他文件
内置函数:
id():获取一个对象的内存地址
type():获取一个对象的数据类型
isinstance():判断一个对象的数据类型是否是指定类型
lst = ['faf',24,6,False,34,True,'535',18]
l1 = [ele for ele in lst if isinstance(ele,int)] # 涉及到继承,继承自int或int都会挑选
print(l1) # [24, 6, False, 34, True, 18]
l2 = [ele for ele in lst if type(ele) == int] # 精确匹配,只挑选int
print(l2) # [24, 6, 34, 18]