一.函数
函数定义
1.将多行代码放在一起,起个名字称为函数定义
2.函数必须先定义后调用
好处:减少代码的冗余,提高程序的编写效率
语法:
def 函数名():
代码
1. def是关键字,用来定义函数的
2. 函数名需要遵守标识符的规则
3. 处于def缩进中的代码,称为函数体
4. 函数定义的时候,函数体中的代码不会执行,在调用的时候才会执行
函数的调用
使用多行代码的时候,称为函数调用
语法:
函数名()
1.函数调用的时候会执行函数体中的代码
2. 函数调用的代码要写在函数体外
# 1. 编写一个打招呼 say_hello 的函数,封装三行打招呼的代码
# 2. 在函数下方调用打招呼的代码
# 函数的定义,不会执行函数中的代码
def say_hello():
print('hello 1')
print('hello 2')
print('hello 3')
# 想要执行函数中的代码,需要调用
say_hello()
组包和拆包
组包(pack):将多个数据值使用逗号连接,组成元组
拆包(unpack):将容器中的数据值使用多个变量分别保存的过程,注意:变量的个数和容器中数据的个数要保持一致
赋值运算符,都是先执行等号右边的代码,执行的结果,保存到等号左边的变量中
# 组包
a, b = 20, 10
c = b, a # 组包
print(type(c), c) # <class 'tuple'> (10, 20)
# 拆包
a, b = c
print(a, b) # 10 20
全局变量和局部变量
局部变量
在函数中定义的变量,称为局部变量
特点:
1.局部变量只能在当前函数内部使用,不能在其他函数和函数外部使用
2.在不同函数中,可以定义名字相同的局部变量,两者之间没有影响
3.在函数被调用的时候局部变量被创建,函数调用结束,局部变量的值被销毁不能使用
所以函数中的局部变量的值,如果想要在函数外部使用,需要使用return关键字,将这个值返回
def func1():
num = 10 # num 就是局部变量
print(f"func1 函数中 {num}")
def func2():
num = 100 # 可以在不同函数中定义名字相同的局部变量,没有影响
print(f"func2 函数中 {num}")
func1() # 10
func2() # 100
func1() # 10
全局变量
在函数外部定义的变量,称为全局变量 特点: 1.可以在任何函数中读取全局变量的值 2.如何在函数中存在和全局变量名字相同的局部变量,在函数中使用的是局部变量的值 3.在函数内部想要修改全局变量的引用,添加global关键字,对变量进行声明为全局变量 4.代码执行的时候被创建,代码执行结束被销毁
g_num = 10 # 全局变量
def func1():
print(f'func1 中 {g_num}') # 在函数中可以读取全局变量的值
def func2():
g_num = 20 # 定义局部变量, 不会影响全局变量
print(f'func2 中 {g_num}')
def func3():
global g_num # 这个函数中使用的 g_num 都是全局变量, 写在函数的第一行
g_num = 30 # 修改了全局变量
print(f'func3 中 {g_num}')
func1() # 10
func2() # 20
func1() # 10
func3() # 30
func1() # 30
函数参数
位置传参
函数调用的时候,按照形参的顺序,将实参值传递给形参
关键字传参
函数调用的时候,指定数据值给形参
混合使用
关键字传参必须写在位置传参后面
不要给一个形参传递多个数据值
def func(a, b, c):
print(f'a: {a}, b: {b}, c: {c}')
# 位置传参
func(1, 2, 3)
# 关键字传参
func(a=2, b=3, c=1)
# 混合使用
func(1, 3, c=5)
缺省参数
缺省参数,默认参数
列表.pop() #不写参数,删除最后一个
列表.sort(reverse=True)
1.定义方式
在函数定义时候给形参一个默认的数据值,这个形参就变为缺省参数,注意缺省参数的书写要放在普通参数后面
2.特点
雀神参数,在函数调用时候,可以传递实参值,也可以不传递实参值
如果传参使用的就是传递的实参值,如果不传参使用的就是默认值
def show_info(name, sex='保密'):
print(name, sex)
show_info('小王')
show_info('小王', '男')
多值参数
不定长位置参数(不定长元组参数)
1.书写,在普通参数的前边,加上一个*, 这个参数就变为不定长位置参数
2.特点,这个形参可以接收任意多个位置传参的数据
3.数据类型,形参的类型是元组
4.注意,不定长位置参数要写在普通的参数的后面
5,一般写法,不定长位置参数的名字为args,即(*args)
不定长关键字参数(不定长字典参数)
1.书写,在普通参数的前边,加上两个*, 这个参数就变为不定长关键字参数
2.特点,这个形参可以接收任意多个关键字传参的数据
3.数据类型,形参的类型是字典
4,注意,不定长关键字参数,要写在所有参数的最后边
5,一般写法,不定长关键字参数的名字为kwargs,即(**kwargs)
完整的参数顺序
def 函数名(普通函数, *args, 缺省参数, **kwargs):
pass
# 一般在使用的时候, 使用 1-2种, 按照这个顺序挑选书写即可
def func(*args, **kwargs):
print(type(args), args)
print(type(kwargs), kwargs)
print('-' * 30)
func()
func(1, 2, 3) # 位置传参, 数据都给 args
func(a=1, b=2, c=3) # 关键字传参, 数据都给 kwargs
func(1, 2, 3, a=4, b=5, c=6)
匿名函数
匿名函数:就是使用lanbda关键字定义的函数 一般称为使用def 关键字定义的函数为标准函数 匿名函数只能书写⼀⾏代码 匿名函数的返回值不需要return,⼀⾏代码(表达式)的结果就是返回值 使⽤场景:作为函数的参数,这个函数⽐较简单,值使⽤⼀次,没有必要使⽤def定义
语法:
lambda参数: 一行代码 # 这一行代码,称为是表达式
# 匿名函数一般不需要我们主动的调用, 一般作为函数的参数使用的
# 我们在学习阶段为了查看匿名函数定义的是否正确,可以调用
# 1, 在定义的时候 ,将匿名函数的引用保存到一个变量中
变量 = lambda参数: 一行代码
# 2. 使用变量进行调用
变量()
# 无参无返回值 打印输出 hello world
def func1():
print('hello world')
func1()
# lambda : print('hello world')
func11 = lambda: print('hello lambda')
func11()
二.面向对象
面向对象是一种编程思想
1.面向过程
2.面向对象
以上两种都属于写代码的套路,最终目的都是为了将代码写出来,只不过过程和思想方法不一样
类和对象
类
抽象的概念, 对 多个 特征和行为相同或者相似事物的统称
泛指的(指代多个,而不是具体的一个)
对象
具体存在的一个事物, 看得见摸得着的
特指的,(指代一个)
类的组成:
1,类名(给这多个事物起一个名字,在代码中满足大驼峰命名法(每个单词的首字母大写))
2,属性(事物的特征,即有什么,一般文字中的名词)
3,方法(事物的行为,即做什么事,一般是动词)
面向对象基本书写
1.定义类
先定义简单的类,不包含属性,在python中定义类需要使用关键字class
⽅法:⽅法的本质是在类中定义的函数,只不过,第⼀个参数是self
class 类名:
# 在缩进中书写的内容,都是类中的代码
def 方法名(self): # 就是一个方法
pass
2.创建对象
创建对象时使用类名()进行创建
类名() #创建⼀个对象,这个对象在后续不能使⽤
#创建的对象想要在后续的代码中继续使⽤,需要使⽤⼀个变量,将这个对象保存起来
变量=类名()
#这个变量中保存的是对象的地址,⼀般可以成为这个变量为对象
#⼀个类可以创建多个对象,只要出现类名()就是创建⼀个对象,每个对象的地址是不⼀样的
3.调用方法
对象.方法名()
例:
列表.sort()
列表.append()
# 1. 创建类
class Person:
# 定义方法, 在类的缩进中定义的函数就是方法
def eat(self):
print('吃饭...')
def drink(self):
print('喝水...')
number1 = Person()
number1.eat()
number1.drink()
self的说明:
1.从函数的语法上讲,self是形参,就可以是任意的变量名,只不过我们习惯性将这个形参写作self
2.self是普通的形参,但是在调⽤的时候没有传递实参值,原因是,Python解释器在执⾏代码的时候,⾃动的将调⽤这个⽅法的对象传递给了self,即self的本质是对象
3.验证,只需要确定通过哪个对象调⽤,对象的引⽤和self的引⽤是⼀样的
4.self是函数中的局部变量,直接创建的对象是全局变量
对象的属性操作
添加属性
对象.属性名 = 属性值
类内部添加
在内部方法中,self是对象
self.属性名 = 属性值
在类中添加属性一般写在_init_方法中
类外部添加
对象.属性名 = 属性值
获取属性
对象.属性名
类内部
在内部方法中,self是对象
self.属性名
类外部
对象.属性名
# 1. 定义类
class Person:
# 定义方法
def eat(self):
print(f'{self.name} 在吃饭')
# 2. 创建对象
tom = Person()
# 添加 name 属性
tom.name = "汤姆"
# 3. 调用方法
tom.eat()
__init__方法
1.什么情况下自动调用
创建对象之后自动调用
2.有什么用,用在哪
1.给对象添加属性的(初始化方法,构造方法)
2.某些代码,在每次创建对象之后,都要执行,就可以将这行代码写在 __init_ _方法(双下划线)
3.书写注意事项
1.如果init方法中存在了self之外的参数,在创建对象的时候必须传参
class Person:
# 定义添加属性方法
def __init__(self,name,age):
self.name = name # 给对象添加属性
self.age = age
__str__方法
1.什么情况下自动调用
使用print(对象)打印对象的时候会自动调用
2.有什么用,用在哪
1.在这个方法中一般书写对象的属性信息的,即打印对象的时候要查看什么信息,在这个方法中进行定义的
2.如果类中没有定义__str__方法(双下划线),print(对象),默认输出对象的引用地址
3.书写注意事项
这个方法必须返回一个字符串
class Person:
# 定义添加属性方法
def __init__(self,name,age):
self.name = name # 给对象添加属性
self.age = age
def __str__(self):
# 方法必须返回的是一个字符串
return f'名字是{self.name},年龄是{self.age}'
__del__方法
__init__方法创建对象之后会自动调用
__del__方法对象被删除销毁时,自动调用
1.调用场景,程序代码运行结束,所有对象都被销毁
2.调用场景,直接使用del删除对象(如果对象有多个名字(多个对象引用一个对象),需要把所有对象都删除才行)
class Demo:
def __init__(self, name):
print('我是 __init__, 我被调用了 ')
self.name = name
def __del__(self):
print(f'{self.name} 没了')
a = Demo('a')
b = Demo('b')
del a # 删除销毁 对象,
print('代码运行结束')
私有和公有
class Demo:
def __init__(self, name, age):
self.name = name
# python 私有的本质是 修改属性的名字, 在私有属性名前添加 _类名 前缀
self.__age = age # 将 age 属性设置为私有的 # __age --> _Demo__age
def __str__(self):
return f"name: {self.name}, age: {self.__age}" # __age --> _Demo__age
# 创建对象
a = Demo('大黄', 2)
print(a.name) # name 是公有属性,可以在类外部使用
# print(a.__age) # __age 是私有属性,不能在类外部使用
a.__age = 10 # 在类外部只能添加和修改公有属性, 添加一个公有属性,名字是 __age
print(a.__age) # 访问的是公有属性 __age
print(a) #
print(a._Demo__age) # 2
a._Demo__age = 3
print(a)
继承
继承描述的是类与类之间的关系
继承的好处:减少代码的冗余(相同的代码不需要多次重复书写),可以直接使用
语法:
#class A(object):
class A: # 没有写父类,但也有父类,object,object类是python中最顶级的类
pass
class B(A): # 类B,继承类A
# 定义一个动物类 Animal类, 方法: 吃
class Animal:
def eat(self):
print('饿了要吃东西....')
# 定义一个狗类 Dog类,继承动物类
class Dog(Animal):
# 在 Dog 类,定义叫的方法
def bark(self):
print('汪汪汪叫.....')
# 定义 Dog类的对象,查看是否可以调用 eat 方法
dog = Dog()
dog.eat() # 子类对象调用父类中的方法
重写
重写:在子类中定义了和父类中名字相同的方法,就是重写
重写的原因:父类中的方法,不能满足子类对象的需求,所以重写
重写之后的特点:调用子类字节的方法,不再调用父类中的方法
重写的方式:
1.覆盖(父类中功能完全抛弃,不要,重写书写)
2.扩展(父类中功能还调用,只是添加一些新的功能)(使用较多)
覆盖:
1.直接在子类中定义和父类中名字相同的方法
2.直接在方法中书写新的代码
扩展:
1.直接在子类中定义和父类中名字相同的方法
2.在合适的地方调用父类中方法super().方法()
class Dog:
def bark(self):
print('汪汪汪叫.....')
class Tom(Dog):
# Tom 类bark 方法不再是汪汪汪叫, 改为 嗷嗷嗷叫
def bark(self):
print('嗷嗷嗷叫...')
# 扩展
# class Tom(Dog):
# def bark(self):
# # 调用父类中的功能
# super().bark()
# super().eat()
# # 书写扩展的新功能
# print('摇尾巴...')
对象的划分
实例对象
1.通过类名()创建的对象,我们称为实例对象,简称实例
2.创建对象的过程称为是类的实例化
3.我们平时所说的对象就是指实例对象(实例)
4.每个实例对象,都有自己的内存空间,在自己的内存空间中保存自己的属性(实例属性)
类对象
1.类对象就是类,或者可以认为是类名
2.类对象是Python解释器在执行代码的过程中创建的
3.类对象的作用:
使用类对象创建实例类名()
类对象也有自己的内存空间,可以保存一些属性值信息(类属性)
4.在一个代码中,一个类只有一份内存空间
属性的划分
实例属性
在init方法中,使用self.属性名=属性值定义
在方法中是使用self.属性名来获取(调用)
类属性
在类内部,方法外部,直接定义的变量,就是类属性
使用: 类对象.属性名=属性值 or 类名.属性名=属性值
类对象.属性名 or 类名.属性名
方法的划分
实例方法
# 在类中直接定义的方法 就是 实例方法
class Demo:
def func(self): # 参数一般写作 self,表示的是实例对象
pass
# 对象.方法名() 不需要给 self 传参
类方法
# 在方法名字的上方书写 @classmethod 装饰器(使用 @classmethod 装饰的方法)
class Demo:
@classmethod
def func(cls): # 参数一般写作 cls, 表示的是类对象(即类名) class
pass
# 1. 通过类对象调用
# 类名.方法名() 也不需要给 cls 传参, python 解释器自动传递
# 2. 通过实例对象调用
# 实例.方法名() 也不需要给 cls 传参, python 解释器自动传递
静态方法
# 在方法名字的上方书写 @staticmethod 装饰器(使用 @staticmethod 装饰的方法)
class Demo:
@staticmethod
def func(): # 一般没有参数
pass
# 1. 通过类对象调用
# 类名.方法名()
# 2. 通过实例对象调用
# 实例.方法名()