一、面向过程与面向对象
面向过程和面向对象都是一种编程方式,只不过再设计上有区别。
1、面向过程pop:
举例:孩子上学
1. 妈妈起床
2. 妈妈洗漱
3. 妈妈做饭
4. 妈妈把孩子叫起来
5. 孩子起床
6. 孩子洗漱
7. 孩子吃饭
8. 妈妈给孩子送学校去
最为典型就是我们的C语言编程。
1》导入各种外部库
2》设计各种全局变量
3》写一个函数
4》写一个函数
5》........
6》写一个main主函数作为程序入口部分
例:从一个列表中删除并返回最后一个元素和删除元素之后的列表
python">def pop_element(lst):if len(lst) > 0:return lst.pop()else:return "列表为空,无法弹出元素"# 示例列表
my_list = [1, 2, 3, 4, 5]# 执行pop操作
result = pop_element(my_list)
print(f"弹出的元素是: {result}")
print(f"当前列表是: {my_list}")
- 面向过程的编程思想将一个功能分解为一个一个小的步骤, 我们通过完成一个一个的小的步骤来完成一个程序
- 这种编程方式,符合我们人类的思维,编写起来相对比较简单
- 但是这种方式编写代码的往往只适用于一个功能, 如果要在实现别的功能,即使功能相差极小,也往往要重新编写代码, 所以它可复用性比较低,并且难于维护
优点:简单直观、性能高效、代码简洁。
缺点:不易维护、不易扩展、代码重用性低。
2、面向对象oop:
举例:孩子上学
“妈妈”送“孩子”上学
python、java、C++都是面向对象的编程方式。
以python为例:从一个列表中删除并返回最后一个元素和删除元素之后的列表
python">class MyList:def __init__(self):self.items = [] # 1》初始化空列表# 2》添加元素def append(self, item):self.items.append(item)# 3》移除并返回最后一个元素(pop操作)def pop(self):if len(self.items) > 0:return self.items.pop()else:return "列表为空,无法弹出元素"# 3》获取当前列表的内容def get_items(self):return self.items# 4》创建MyList对象
my_list_obj = MyList()# 5》向列表添加元素
my_list_obj.append(1)
my_list_obj.append(2)
my_list_obj.append(3)# 6》执行pop操作
result = my_list_obj.pop()
print(f"弹出的元素是: {result}")
print(f"当前列表是: {my_list_obj.get_items()}")
- 面向对象的编程思想,将所有的功能统一保存到对应的对象中 比如,妈妈功能保存到妈妈的对象中,孩子的功能保存到孩子对象中 。要使用某个功能,直接找到对应的对象即可
- 这种方式编写的代码,比较容易阅读,并且比较易于维护,容易复用。
- 但是这种方式编写,不太符合常规的思维,编写起来稍微麻烦一点
优点:模块化、安全性高、代码重用性高。
缺点:学习难度大、性能开销大、调试困难。
二、类和对象
目前我们所学的python中的内置类型都是对象——内置对象,内置对象有时候不能满足我们的需求,需要自定义一些对象。
10, 20, 30, -40, a=10, b=20等等等,都是整数,对其进行抽象——int类。
python 中一切都是对象。
类也是一个对象。
1、类的定义与实例化对象
在python中使用class关键字创建一个类。
语法格式a:
python">class ClassA:# 公共的属性def __init__(self):passdef fun1(self):passdef fun2(self):pass
语法格式b:
python">class ClassA(object):# 公共的属性def __init__(self):passdef fun1(self):passdef fun2(self):pass
实例化对象的语法:
1》无参
对象名 = 类名()
2》有参
对象名 = 类名(参数列表)
例: 输出汽车品牌型号和问世年份
python"># 定义一个简单的类
class Car(object):brand = "比亚迪" # 实例属性model = "秦L DM"year = "2024"def __init__(self):passdef display_info(self): # 实例方法pass# 实例化对象
s1 = Car()
s2 = Car()
s3 = Car()
print(s1.brand)
print(s2.model)
print(s3.year)# 输出:品牌: 比亚迪, 型号: 秦L DM, 年份: 2024
第一个 __init__()方法是一种特殊的方法,被称为类的构造函数或初始化方法,当创建了这个类的实例时就会自动调用该方法。不是必须要定义的,可以在需要的时候再定义。
第二个 self是一个参数,表示对象自身【对象的id号】,里面存放着对象自身的地址。如果希望类中的方法可以被对象调用,第一个参数必须是self。作用是将实例对象与类的方法进行绑定,这样实例的每个对象都能调用“属于”自己的方法。传参时可以省略。
python">class Cat(object):def speak(self):print(f"{self}在喵喵喵")d1 = Cat()
d2 = Cat()
d1.speak()
d2.speak()
2、访问属性/方法
使用符号 . 进行访问
访问属性:
对象名.属性
访问方法:
对象名.方法名()
3、对象与类的关系
- 对象拥有类的所有属性和方法
- 对象的属性和方法可以单独添加、删除、修改
- 对象与对象之间的属性和方法不可共享
- 对象不能独自创建,必须依托于类,类可以实例化N个对象
python">class Stu:def __init__(self, name, ability):self.name = nameself.ability = abilitydef bark(self):print(f"{self.name}在打游戏!")# Stu 是类,stu1 和 stu2 是对象
stu1 = Stu("马斯轩", "会吃饭")
stu2 = Stu("唐朗明", "会学习")stu1.bark() # 输出:马斯轩在打游戏!
stu2.bark() # 输出:马斯轩在打游戏!
还可以使用以下函数的方式来访问属性:
- getattr(obj, name[, default]) : 访问对象的属性。
- hasattr(obj,name) : 检查是否存在一个属性。
- setattr(obj,name,value) : 设置一个属性。如果属性不存在,会创建一个新属性。
- delattr(obj, name) : 删除属性。
4、魔方方法——构造函数 与 析构函数
- __init__ 构造函数:完成对象的初始化工作,方便统一管理、调用类创建对象时,自动执行。
- __del__ 析构函数:删除对象时执行一些操作,自动执行。
- __str__ 打印方法:输出执行信息,自动执行。
python">class Car:def __init__(self, name):self.name = namedef __str__(self):return f"轿车{self.name}是黑色的。"def __del__(self):print(f"轿车{self.name}报废了,去车管所吧!")# 创建并销毁对象
car = Car("奥迪")
print(car) #轿车奥迪是黑色的。
del car # 输出:轿车奥迪报废了,去车管所吧!
5、类属性/方法 与 实例对象属性/方法 与 静态方法
示例:类属性和方法、实例属性和方法、静态方法
python">class Vehicle:wheels = 4 # 类属性def __init__(self, brand):self.brand = brand # 实例属性def drive(self): # 实例方法print(f"正在开着{self.brand}的车")@classmethoddef wheels_info(cls): # 类方法print(f"一辆车拥有{cls.wheels}个轮胎。")@staticmethoddef general_info(): # 静态方法print("买车是为了开的。")# 创建实例对象
car = Vehicle("比亚迪")# 访问实例属性和方法
print(car.brand) # 输出:比亚迪
car.drive() # 输出:正在开着比亚迪的车# 访问类属性和类方法
print(Vehicle.wheels) # 输出:4
Vehicle.wheels_info() # 输出:一辆车拥有4个轮胎。# 调用静态方法
Vehicle.general_info() # 输出:买车是为了开的。
6、Python的内置类属性
- __dict__ : 类的属性(包含一个字典,由类的数据属性组成)
- __doc__ :类的文档字符串
- __name__: 类名
- __module__: 类定义所在的模块(类的全名是'__main__.className',如果类位于一个导入模块mymod中,那么className.__module__ 等于 mymod)
- __bases__ : 类的所有父类构成元素(包含了一个由所有父类组成的元组)
python">class Student(object):"""定义一个学生类属性:名字 年龄方法:method_1 method_2"""name = '张三'age = 18def method_1(self):passdef method_2(self):passprint(Student.__dict__) #输出:{'__module__': '__main__', '__doc__': '\n 定义一个学生类\n 属性:名字 年龄\n 方法:method_1 method_2\n ', 'name': '张三', 'age': 18, 'method_1': <function Student.method_1 at 0x00000189521B8AE0>, 'method_2': <function Student.method_2 at 0x00000189521B8C20>, '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>}
print(Student.__doc__) #输出:
# 定义一个学生类
# 属性:名字 年龄
# 方法:method_1 method_2
print(Student.__name__) #输出:Student
print(Student.__module__) #输出:__main__
print(int.__module__) #输出:builtins
print(Student.__bases__) #输出:(<class 'object'>,)
三、类的封装【私有属性与方法】
封装是类的三大特性之一。
封装指的是隐藏对象中一些不希望让外部所访问的属性或方法。
python中封装其实是通过设置访问权限来体现的,私有属性和私有方法是控制访问权限的组成部分。
1、私有属性
在类的内部使用,不希望外部直接访问的变量。
在python中,使用双下划线作为前缀来定义私有属性。
私有属性在类外不能访问
python">class Person:def __init__(self, name, age):self.__name = name # 私有属性self.__age = age # 私有属性def display_info(self):print(f"Name: {self.__name}, Age: {self.__age}")def set_name(self, name): # 通过公共方法设置私有属性self.__name = namedef get_name(self): # 通过公共方法访问私有属性return self.__name# 创建对象
person = Person("小花", 30)# 访问私有属性会报错
# print(person.__name) # AttributeError: 'Person' object has no attribute '__name'# 通过公共方法访问和修改私有属性
print(person.get_name()) # 输出:小花
person.set_name("小明")
print(person.get_name()) # 输出:小明
2、私有方法
和私有属性是一样的。
python">class Car:def __init__(self, brand):self.__brand = branddef __start_engine(self): # 私有方法print(f"{self.__brand}启动了!")def drive(self):self.__start_engine() # 通过公有方法调用私有方法print(f"正在驾驶着{self.__brand}的车!")# 创建对象
car = Car("比亚迪")# 访问私有方法会报错
# car.__start_engine() # AttributeError: 'Car' object has no attribute '__start_engine'# 通过公共方法间接调用私有方法
car.drive()
# 输出:
# 比亚迪启动了!
# 正在驾驶着比亚迪的车!
3、属性装饰器
属性装饰器是实现把方法转为属性的装饰器。
作用:
- 把方法转为属性,便于操作属性
- 实现对属性的更改(验证)、查看、删除
语法格式:
python">class 类名(object):def __init__(self):self.__名字 = xxx@propertydef 函数名(self):return self.__名字@函数名.setterdef 函数名(self, m):self.__名字 += m
python">class Car(object):def __init__(self, name, color):self.name = nameself.color = colorself.__num = 20@propertydef num(self):return self.__num@num.setterdef num(self, x):self.__num += xc1 = Car('法拉利', '红色')
print(c1.name, c1.color, c1.num)
c1.num = 80
print(c1.num)
python">class Student(object):def __init__(self, name):self.__name = name@propertydef name(self):return self.__name@name.setterdef name(self, new_name):if not isinstance(new_name, str):print('名字必须是一个字符串')else:self.__name = new_name@name.deleterdef name(self):print("已删除名字")del self.__names1 = Student('张三')
print(s1.name)
s1.name = 111
s1.name = '华清远见'
print(s1.name)
del s1.name
四、类的继承
面向对象的编程带来的主要好处之一就是代码的重用,实现这种重用的方法之一就是通过继承机制。
通过继承创建的新类称之为【子类】或者【派生类】,被继承的类称之为【父类】、【基类】、【超类】。
1、继承语法格式
python">class 子类名(父类名列表):pass
举例说明
python">class Parent(object):"""定义父类"""par_attr = 100def __init__(self):print("初始化父类")def par_fun1(self):print("父类方法1")def par_fun2(self):print("父类方法2")class Child(Parent):"""定义子类"""child_attr = 666def __init__(self):print("初始化子类")def child_fun1(self):print("子类方法1")c1 = Child()
print(c1.child_attr)
c1.child_fun1()print(c1.par_attr)
c1.par_fun1()
c1.par_fun2()
2、多继承语法
如果在继承的元组()里面有一个以上的类,就称之为多继承。
python">class A:passclass B:passclass C:passclass D(A, B, C):passd1 = D()
print(D.mro())
# python中提供了一个函数mro()用于查看继承的顺序。
3、继承重写父类方法
如果你的父类方法不能满足你得要求,你可以在之类中重写父类的方法。
python">class Parent(object):def method(self):print(f"{self}的方法")class Child(Parent):passc = Child()
c.method()# 重写
class Parent(object):def method(self):print(f"{self}的方法")class Child(Parent):def method(self):print("xxxxxxxxxx")print(f"{self}的方法")c = Child()
c.method()
这里列出了一些通用的功能,可以在自己的类重写:
- __init__ ( self [,args...] ) 构造函数 简单的调用方法: obj = className(args)
- __del__( self ) 析构方法, 删除一个对象 简单的调用方法 : del obj
- __repr__( self ) 转化为供解释器读取的形式 简单的调用方法 : repr(obj)
- __str__( self ) 用于将值转化为适于人阅读的形式 简单的调用方法 : str(obj)
- __cmp__ ( self, x ) 对象比较 简单的调用方法 : cmp(obj, x)
4、python继承特点
- 在子类中如果需要父类的构造方法,需要显式调用父类的构造方法,或者不重写父类的构造方法。__init__()
- 在子类中调用父类的方法,需要显式调用,且第一个参数self不能省略。
python">class Parent(object):def __init__(self, x, y):self.x = xself.y = ydef method(self):print(f"{self}的方法")class Child(Parent):def fun1(self):print(self.x , self.y)
# 不重写父类构造方法
c = Child(1, 2)
c.fun1()
python">class Parent(object):def __init__(self, x, y):self.x = xself.y = ydef method(self):print(f"{self}的方法")class Child(Parent):def __init__(self, x, y, z):Parent.__init__(self, x, y)self.z = zdef fun1(self):print(self.x , self.y, self.z)# 重写父类构造方法、里面显式调用父类构造方法
c = Child(1, 2, 33)
c.fun1()
python">class Parent(object):def __init__(self, x, y):self.x = xself.y = yclass Child(Parent):def __init__(self, x, y, z):super().__init__(x, y)self.z = zdef add(self):return self.x + self.y + self.zc = Child(1, 1, 1)
print(c.add())
python">class Parent(object):def __init__(self, x, y):self.x = xself.y = ydef add(self):return self.x + self.yclass Child(Parent):def __init__(self, x, y, z):super().__init__(x, y)self.z = zdef add(self):return super().add() + self.zc = Child(1, 1, 1)
print(c.add())
5、运算符重载
在Python中,并没有像其他语言(如C++)中那样的内置机制来重载运算符。但是,你可以通过定义特定的方法来模拟运算符重载的行为。
以下是一些常见运算符以及它们对应的特殊方法:
加法:+ 对应 __add__
减法:- 对应 __sub__
乘法:* 对应 __mul__
除法:/ 对应 __truediv__
取模:% 对应 __mod__
幂运算:** 对应 __pow__
位运算:
位运算:>> 对应 __rshift__
位运算:& 对应 __and__
位运算:| 对应 __or__
位运算:^ 对应 __xor__
python">class Operator(object):def __init__(self, x):self.x = xdef __add__(self, other):return self.x + other.x * 3o1 = Operator(1)
o2 = Operator(3)
print(o1 + o2)
python">class Operator(object):def __init__(self, x, y):self.x = xself.y = ydef __add__(self, other):return self.x * other.x + self.y * other.yo1 = Operator(1, 2)
o2 = Operator(3, 4)
print(o1 + o2)
五、类的多态
python中的多态也可以通过方法重写进行。
同一个方法,不同对象显式的结果不同。
python">class Animal(object):name = '动物'age = 0def speak(self):print("动物的声音")class Dog(Animal):def speak(self):print("汪汪汪")class Cat(Animal):def speak(self):print("喵喵喵")a = Animal()
d = Dog()
c = Cat()a.speak()
d.speak()
c.speak()
六、关于下划线说明
- __foo__: 以双下划线开头双下划线结尾,定义的是特殊方法,一般是系统定义名字 ,类似 __init__() 之类的,自动。
- _foo: 以单下划线开头的表示的是 protected 类型的变量,即保护类型只能允许其本身与子类进行访问,不能用于 from module import ···
- __foo: 双下划线的表示的是私有类型(private)的变量, 只能是允许这个类本身进行访问了。