Python函数

发布于:2024-10-16 ⋅ 阅读:(23) ⋅ 点赞:(0)

文章目录

一、函数基本语法

二、高阶函数

三、装饰器

四、包和模块

五、面向对象

        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]