函数+类+对象
函数
与java不同的是,参数并不用事先声明类型
def add(x, y, z):
return x + y + z
print(add(1, 2, 3))
全局变量和局部变量
简单的来说,局部变量是在函数内定义的变量,只能在定义它的函数中使用。
全局变量,是定义在函数之外的变量,它可以在程序的任何位置使用。
默认参数
def add(x, y, z, m=10):
return x + y + z + m
print(add(1, 2, 3))
print(add(1, 2, 3, 4))
16
10
不定长参数
元组形式导入(*)
在创建函数时,将参数前面加上*
,加上*号的参数被接收后会以元组
的形式导入。
def add(a, *b):
print(a)
print(b)
for i in b:
print(i)
add(1, 2, 3, "string", "new")
1
(2, 3, 'string', 'new')
2
3
string
new
字典形式导入(**)
def add(a, **kwargs):
print(a)
print(kwargs)
for i in kwargs.items():
print(i)
add(1, b=2, c=3, e="hello")
结果
{'b': 2, 'c': 3, 'e': 'hello'}
('b', 2)
('c', 3)
('e', 'hello')
匿名函数
通过使用lambda
来创建匿名函数,它是一种无需定义函数名的函数,可以通过变量来记录该函数。
相比普通函数,其只能是单个表达式
,即lambda函数的语法中只包含一个语句,所以其功能比较单一
且不能被其它程序使用。
add = lambda a, b, c: a * b / c * 0.5
print(add(20, 30, 60))
结果5.0
递归函数
递归函数
,它可直接或间接地调用函数自身。其中函数递归时,要有递归公式和边界条件,递归公式用于分解规模较大的问题为规模较小的问题,边界条件用于终止递归函数。
def f(x):
if x == 1:
return 1
else:
return x + f(x - 1)
print(f(3))
结果6
思路
f(3)=3+f(2)
f(2)=2+f(1)
f(1)=1
再逆推
f(2)=2+f(1)=3
f(3)=3+f(2)=6
类与对象
类的定义+对象的创建和使用
class My:
s = "zhangsan"
def my(self, i):
print(i)
if __name__ == "__main__":
a = My()
print(a.s)
a.my("string")
私有成员和私有方法的定义和使用
定义类中的属性和方法默认为公有属性
和公有方法
,该类中的对象可以任意访问类中的公有成员和公有方法,可以通过将成员和方法定义为私有,即私有成员和私有方法,从而限制对象对类的访问
。
通过在类成员名称或类方法名称前面加上双下划线__
限制成员的访问,即定义为私有成员和私有方法
这里要注意,实例化的对象是无法直接访问类中的私有成员和私有方法的,它们都可通过公有方法中的参数self调用
。
class My:
__s = "zhangsan"
def __my(self, i):
print(i)
def order(self):
print("共有方法通过self访问私有成员" + self.__s)
self.__my("hello")
if __name__ == "__main__":
a = My()
a.order()
结果
共有方法通过self访问私有成员zhangsan
hello
构造方法和析构方法
类中有两个名为 __init__()
和__del__()
的特殊方法,它们分别是构造方法
和析构方法
,该两种方法在类创建
和销毁
时会自动调用
。
构造方法
每个定义的类中都有一个默认的__init__()构造方法,如果在定义类时未定义,则系统会调用默认的构造方法,而如果用户在定义时显式地定义了构造方法,则会调用该定义的构造方法。
可以通过无参构造方法
和有参构造方法
来分别对该方法创建的对象赋予相同
的初始值和不同
的初始值。
注:这里按照参数的有无分为两种构造方法,但除了self,因为类中必须包含参数 self, 且为第一个参数。
class My:
def __init__(self, a, b):
self.a = a
self.b = b
def order(self):
s = self.a * self.b / 2
return s
if __name__ == "__main__":
a = My(3, 4)
print(a.order())
结果6.0
析构方法
对象被清理时,系统会自动调用析构方法,即对象在内存中被释放时自动触发执行__del__()析构方法
。
class My:
def __init__(self, a, b):
self.a = a
self.b = b
print("__init__构造函数被执行")
def __del__(self):
print("__del__析构函数被执行")
def order(self):
s = self.a * self.b / 2
return s
if __name__ == "__main__":
a = My(3, 4)
print(a.order())
结果
__init__构造函数被执行
6.0
__del__析构函数被执行
类方法和静态方法
在使用 Python 编程时,一般不需要使用类方法或静态方法,程序完全可以使用函数来代替类方法或静态方法。但是在特殊的场景(比如使用工厂模式)下,类方法或静态方法也是不错的选择。
类方法
类方法通过装饰器@classmethod修饰
,它的第一个参数并非类本身self,而是cls
。
与实例方法不一样,它既可由类实例化的对象调用,也可由类调用,且类方法可以修改类属性
,而实例方法不能。
简单的说,你调用了类方法修改类属性A,该类的所有实例的属性A都被修改
但是,如果你通过实例B调用实例方法修改了属性A,则只有实例B的A属性变更了,其余实例没有变化。
通过类.类方法调用
class My:
str = "hello world"
@classmethod
def leiFangFa(cls):
cls.str = "lisi" # 类方法可以修改类属性
@classmethod
def leiFangFaTwo(cls):
print(cls.str)
def shiLiFangFaTwo(self):
self.str = "wangwu" # 实例方法修改了str,只能修改实例的这个str,不能修改类的str
def shiLiFangFaThree(self):
print(self.str) # 实例方法,使用了类属性
if __name__ == "__main__":
my = My()
my2 = My()
# My.shiLiFangFaTwo() 报错TypeError: shiLiFangFaTwo() missing 1 required positional argument:
# 'self',因为实例方法一个有一个参数是self,但是类方法是没有的
print("第一次调用实例方法,str获取hello world")
my.shiLiFangFaThree()
my2.shiLiFangFaThree()
print("调用类方法,修改了类属性,不管有多少个实例,它的str都变成了lisi")
My.leiFangFa()
print("第二次调用实例方法,它的str都变成了lisi")
my.shiLiFangFaThree()
my2.shiLiFangFaThree()
print("my实例通过实例方法,修改了str的值为wangwu")
my.shiLiFangFaTwo()
print("my实例通过实例方法,打印str的值为wangwu")
my.shiLiFangFaThree()
print("my2实例通过实例方法,打印str的值并没有与my实例的保持一致,而是打印的是lisi,为类的属性")
my2.shiLiFangFaThree()
print("再次调用类方法,获取str的值,没有被实例方法shiLiFangFaTwo改变,仍然是lisi")
My.leiFangFaTwo()
结果
第一次调用实例方法,str获取hello world
hello world
hello world
调用类方法,修改了类属性,不管有多少个实例,它的str都变成了lisi
第二次调用实例方法,它的str都变成了lisi
lisi
lisi
my实例通过实例方法,修改了str的值为wangwu
my实例通过实例方法,打印str的值为wangwu
wangwu
my2实例通过实例方法,打印str的值并没有与my实例的保持一致,而是打印的是lisi,为类的属性
lisi
再次调用类方法,获取str的值,没有被实例方法shiLiFangFaTwo改变,仍然是lisi
lisi
通过实例.类方法调用
类.实例方法会报错,那实例.类方法呢
class My:
str = "hello world"
@classmethod
def leiFangFa(cls):
cls.str = "lisi" # 类方法可以修改类属性
@classmethod
def leiFangFaTwo(cls):
print(cls.str)
def shiLiFangFaThree(self):
print(self.str)
if __name__ == "__main__":
my = My()
my2 = My()
print("第一次:首先两个实例分别打印出str的值,均为hello world")
my.shiLiFangFaThree()
my2.shiLiFangFaThree()
print("第一次:通过类调用类方法获取str的值,为hello world")
My.leiFangFaTwo()
print("通过实例调用类方法,修改了类属性str的值为lisi")
my.leiFangFa()
print("第二次:再次通过两个实例获取str的值")
my.shiLiFangFaThree()
my2.shiLiFangFaThree()
print("第二次:通过类调用类方法获取str的值,为lisi")
My.leiFangFaTwo()
结果
第一次:首先两个实例分别打印出str的值,均为hello world
hello world
hello world
第一次:通过类调用类方法获取str的值,为hello world
hello world
通过实例调用类方法,修改了类属性str的值为lisi
第二次:再次通过两个实例获取str的值
lisi
lisi
第二次:通过类调用类方法获取str的值,为hello world
lisi
类方法的使用场景
类方法用在模拟java定义多个构造函数的情况。 由于Python类中只能有一个初始化方法,不能按照不同的情况初始化类。
说实在我没看明白是怎么实现的,可能后续能反应过来,所以先记录上
class Book():
def __init__(self, title):
self.title = title
@classmethod
def create(cls, title):
book = cls(title=title)
return book
book1 = Book("python")
book2 = Book.create("python and django")
print(book1.title)
print(book2.title)
python
python and django
静态方法
首先静态方法与类方法一样,既可由类实例化的对象调用,也可由类调用。静态方法没有self参数,是通过装饰器@staticmethod修饰
。
由于没有默认参数,所以它无法使用默认参数来访问类成员。
静态方法的优点:
1、它消除了self
参数的使用。
2、它提高了代码的可读性,表示该方法不依赖于对象本身的状态。
静态方法的用途有限,因为它们不能访问类实例的属性(像常规方法那样),也不能访问类本身的属性(像类方法那样)。
所以它们对日常方法没有用处。
但是,它们可以用于将一些实用程序函数与一个类(例如,从一种类型到另一种类型的简单转换)组合在一起,该类不需要访问除了所提供的参数以外的任何信息(可能还有一些对模块全局的属性)
它们可以放在类之外,但是在类内部对它们进行分组可能有意义,因为它们只适用于类。
您还可以通过实例或类而不是模块名来引用方法,这可能有助于读者理解该方法与哪个实例相关。
class Bird:
# classmethod修饰的方法是类方法
@classmethod
def fly (cls):
print('类方法fly: ', cls)
# staticmethod修饰的方法是静态方法
@staticmethod
def info (p):
print('静态方法info: ', p)
# 调用类方法,Bird类会自动绑定到第一个参数
Bird.fly() #①
# 调用静态方法,不会自动绑定,因此程序必须手动绑定第一个参数
Bird.info('crazyit')
# 创建Bird对象
b = Bird()
# 使用对象调用fly()类方法,其实依然还是使用类调用,
# 因此第一个参数依然被自动绑定到Bird类
b.fly() #②
# 使用对象调用info()静态方法,其实依然还是使用类调用,
# 因此程序必须为第一个参数执行绑定
print("-------")
b.info('fkit')
结果
类方法fly: <class '__main__.Bird'>
静态方法info: crazyit
类方法fly: <class '__main__.Bird'>
-------
静态方法info: fkit
继承
类和类可以继承,继承(派生)的类的称为派生类或子类
,被继承的类称为基类或父类
。
继承的实现
class teacher:
teacher_name = "teacher"
def get_teacher_name(self):
print(self.teacher_name)
def get_sex(self):
print("nan")
class student(teacher):
student_name = "student"
def get_student_name(self):
print(self.student_name)
def get_teacher_name_two(self):
print(self.teacher_name)
def get_sex(self):
print("nv")
def get_super(self):
super().get_sex()
if __name__ == "__main__":
teacherX = teacher()
studentX = student()
print("通过老师的实例获取teacher_name")
teacherX.get_teacher_name()
print("通过老师的实例调用get_sex")
teacherX.get_sex()
print("通过学生的实例获取student_name")
studentX.get_student_name()
print("通过学生的实例调用父类的get_teacher_name方法获取从父类继承来的teacher_name")
studentX.get_teacher_name()
print("通过学生的实例获取从父类继承来的teacher_name")
studentX.get_teacher_name_two()
print("父类与子类有同样的方法,覆盖了父类的方法")
studentX.get_sex()
print("子类通过super()调用父类的方法,可以通过super()函数,在派生类重写基类的方法后,仍可调用基类中的方法。")
studentX.get_super()
结果
通过老师的实例获取teacher_name
teacher
通过老师的实例调用get_sex
nan
通过学生的实例获取student_name
student
通过学生的实例调用父类的get_teacher_name方法获取从父类继承来的teacher_name
teacher
通过学生的实例获取从父类继承来的teacher_name
teacher
父类与子类有同样的方法,覆盖了父类的方法
nv
子类通过super()调用父类的方法,可以通过super()函数,在派生类重写基类的方法后,仍可调用基类中的方法。
nan
一个类可以继承多个父类
这一点是和java不也一样的,java是可以只能继承父类,但是可以实现多个接口
多态
多态指不考虑对象类型并使用该对象,让具有不同功能的函数使用相同的函数名称,从而通过函数名称调用不同功能的函数。
class Person:
def get_word(self, name):
name.get_word()
class Employee:
def get_word(self):
print("职工")
class Teacher(Employee):
def get_word(self):
print("教师")
class Professor(Employee):
def get_word(self):
print("教授")
if __name__ == "__main__":
A = Person()
A.get_word(Employee())
A.get_word(Teacher())
A.get_word(Professor())
结果
职工
教师
教授
从上面的演示来看,python的多态和java的相差很大
java是通过方法的入参为接口或者父类,在运行的时候才知道传入的是哪个子类或者实现类,但是python的多态竟然是通过一个在所有类之外新建一个类来实现,很奇怪的方式,我有点不太理解。
枚举类
from enum import Enum, unique
@unique
class WeekDay(Enum): # 继承了Enum枚举
Mon = 1
Tue = 2
Wed = 3
Thu = 4
Fri = 5
Sat = 6
Sun = 7
print(WeekDay.Wed)
print(WeekDay.Wed.value)
print(WeekDay(7))
print(WeekDay['Sat'])
day = WeekDay.Fri
print(day == WeekDay.Fri)
WeekDay.Wed
3
WeekDay.Sun
WeekDay.Sat
True