面向对象(OOP)是一种编程范式,它使用''对象''来设计软件。对象可以包含数据和代码:数据代表对象的状态,而代码代表操作数据的方式。在面向对象编程中,一切皆对象,这意味着将现实世界事务使用类与实例来模拟,如灯,汽车,导弹,杯子,都可以用类和实例来模拟。
一 、类与实例
类
类是对现实世界描述的一种类型。它定义了一组具有相同属性和方法的对象的结构。类名通常使用大驼峰命名法,例如 ElectricCar
。
class ElectricCar: pass
类是抽象的,约定了未来实例应该有的内容,是实例的模板。
实例
实例是类的具现化。通过调用类来生成实例:
class ElectricCar: pass my_car = ElectricCar()
实例是具体的,具有具体的数据。实例的内容依赖于类。
二、self
self
是类的一个特殊变量,用于代表未来的实例。在类的内部,self
用于访问类的属性和方法。
初始化函数
__init__
是一个特殊的方法,称为构造器,用于初始化新创建的对象。
class ElectricCar: def __init__(self, make, model): self.make = make self.model = model
魔法函数
Python 提供了一系列特殊的方法,称为魔法函数,例如 __str__
、__len__
和比较方法 __eq__
等。
# 内部编写所有魔法方法的使用案例 ''' 以双下滑线开头和结尾的为魔法函数 __init__: 用于初始化对象。 __str__: 返回实例用字符串表示,自定义内容 __len__:当使用 len() 函数时调用,返回容器类型的长度。 实例与实例之间也可以比较了 __eq__ :==触发 __ne__ :!=触发 __gt__ :>触发 __ge__ :>=触发 __lt__ :< 触发 __le__ :<=触发 __add__ :+触发 __sub__ :-触发 __mul__ :*触发 __truediv:/触发 __floordiv__://触发 __mod__:%触发 __divmod__:div(x,y)触发 ''' # class Myclass: # def __init__(self, name, age): # print('初始化函数执行了') # self.name = name # self.age = age # # def __str__(self): # return f'姓名:{self.name},年龄:{self.age}' # # def __len__(self): # return len(self.name) # # def __gt__(self, other): # ''' # 用>来判断年龄大小 # :param other: # :return: # ''' # return self.age > other.age # # def __lt__(self, other): # ''' # 用<来判断 年龄大小 # :param other: # :return: # ''' # return self.age < other.age # # def __ge__(self, other): # ''' # 用>=来判断 年龄大小 # :param other: # :return: # ''' # return self.age >= other.age # # def __le__(self, other): # ''' # 用<=来判断 年龄大小 # :param other: # :return: # ''' # return self.age <= other.age # # def __eq__(self, other): # ''' # 判断两个实例age和name是否完全相等 # :param other: # :return: # ''' # return self.age == other.age and self.name == other.name # # def __ne__(self, other): # ''' # 判断两个实例 age 或者 name 是否有一方不相等 # :param other: # :return: # ''' # return self.age != other.age or self.name != other.name # # def __add__(self, other): # ''' # 返回两个实例的age相加 # :param other: # :return: # ''' # return self.age + other.age # # def __mul__(self, other): # ''' # 返回两个实例的age乘积 # :param other: # :return: # ''' # return self.age * other.age # # def __divmod__(self, other): # ''' # 先求除 再求余 # :param other: # :return: # ''' # return divmod(self.age, other.age) # # def __mod__(self, other): # ''' # 返回两个实例的 # :param other: # :return: # ''' # return self.age % other.age # # # mc = Myclass('亲亲亲', 18) # print(mc) # # __len__ # print(len(mc)) # # mc2 = Myclass('顺子', 18) # # __str__ # print(mc2) # __gt__ # print(mc > mc2) # # __lt__ # print(mc < mc2) # # __ge__ # print(mc >= mc2) # __le__ # print(mc <= mc2) # __eq__ # print(mc == mc2) # # __nq__ # print(mc != mc2) # # __add__ # print(mc + mc2) # # __mul__ # print(mc * mc2) # __mod__ # print(mc % mc2) # __divmod__ # print(divmod(mc, mc2))
构造函数与析构函数
构造函数用于创建实例,返回实例,通过父类来创建实例,super()._new()而析构函数在实例不再使用时执行,用于清理资源。
""" 构造函数:创建并且返回实例(self) 初始化函数: self已经创建完成,可以向self中添加数据 析构函数: 销毁实例 清理实例内存 实例不在使用则回收实例内存之前汇执行对应的析构函数 """ # class MyClass: # def __new__(cls, *args, **kwargs): # # 调用父类的new方法创建一个实例 # instance = super().__new__(cls) # print(f"构造函数执行了", id(instance)) # # 将创建好的实例返回 返回给初始化函数 # return instances # # def __init__(self, name): # print(f"初始化函数执行了", id(self)) # self.name = name # # def __del__(self): # print(f"析构函数执行了") # # # mc1 = MyClass("阿拉伯") # print(id(mc1), id(None), mc1 is None) # # mc1 = None # print("程序执行完毕 将要退出") # # 程序退出执行析构mc1 class MyOpen: def __init__(self, filename, mode="r", encoding="utf8"): self.f = open(filename, mode=mode, encoding=encoding) def read_all(self): return self.f.read() def __del__(self): self.f.close() mo = MyOpen("./65.魔法函数.py") print(mo.read_all())
三、三大特性
封装
封装是将数据(属性)和操作数据的方法组合在一起的过程。封装确保了数据的安全性,只能通过特定的方法来访问和修改。
# 装饰器:一个函数,用于增强或修改另一个函数的行为,通常通过返回一个新的函数来实现。 import random import time datas = [random.randint(0, 10000) for i in range(10000)] # 通过浅拷贝 得到一模一样的列表 datas_copy = datas.copy() def time_cost(f): def calc(): stat = time.time() f() print(f'{f}花费的时间开销为:{time.time() - stat}') return calc @time_cost def my_fun1(): datas.sort() print(datas) my_fun1() @time_cost def my_fun2(): new_list = sorted(datas_copy) print(new_list) my_fun2() import random import time datas = [random.randint(0, 10000) for i in range(10000)] # 通过浅拷贝 得到一模一样的列表 datas_copy = datas.copy() def time_cost(f): def calc(): stat = time.time() f() print(f'{f.__name__}花费的时间开销为:{time.time() - stat}') return calc def fun1(): datas.sort() print(datas) fun1 = time_cost(fun1) fun1() # 此时的fun1()不是fun1()而是calc() def fun2(): new_list = sorted(datas_copy) print(new_list) fun2 = time_cost(fun2) fun2() # 此时的fun2()不是fun2()而是calc()
继承
继承允许新创建的类(子类)继承现有类(父类)的属性和方法。
- __base__ :获取父类
- __bases__ :获取父类元组
- __class__ :返回实例对应的类
多继承
继承多个父类-java,c# 只支持单继承,通过接口等来实现多继承的功能,Python直接就可以有多个父类。
# 多继承 继承多个父类 class SpeakAble: def __init__(self, la): self.la = la def speak(self): return print(f'I can speak {self.la}') class SpeedAble: def __init__(self, speed): self.speed = speed def move(self): print(f'Moving at a speed of {self.speed} km/h') class Person(SpeedAble, SpeakAble): def __init__(self, name, la, speed): self.name = name SpeedAble.__init__(self, speed) SpeakAble.__init__(self, la) def __str__(self): return f'name: {self.name}, language: {self.la}, speed: {self.speed}' class ATM(Person): def __init__(self, name, speed, la, skill): super().__init__(name, la, speed) self.skill = skill def show(self): return print(f'{self.name},{self.speed},{self.la},{self.skill}') def attack(self): print(f'{self.name} is attacking with a skill: {self.skill}') atmo = ATM('赛罗', 1000, '光环语言', '赛罗光纤') atmo.show() atmo.move() atmo.speak() atmo.attack() # 多继承 mro : method(方法) retrieval(检索) order(顺序) # Python3 使用广度优先 # print(ATM.mro())
多态
多态允许同一个接口接受不同的数据类型。在Python中,多态是隐式实现的,不需要显式定义。
# 同名不同参数 # def my_fun(a): # print(a) # # # def my_fun(a, b): # print(a, b) # # # # 后一个的my_fun()覆盖掉了上方的my_fun # my_fun(10, 2) # 在面向对象编程中,子类可以重写父类的方法,当调用子类实例的方法时,将执行子类中的方法定义。 class Animal: def walk(self, speed): print('walk') class Dog(Animal): def walk(self, speed): print('run') class Cat(Animal): def walk(self, speed): print('sheep') # 类似于多态!结果以元组的形式输出 def my_fun(*args): print(args) my_fun(10) my_fun(10, 11) my_fun(10, 11, 22) my_fun(10, 11, 22, 33)
抽象类
抽象类是一种特殊的类,它不能被实例化,但可以包含抽象方法。子类继承抽象类时,必须实现这些抽象方法。
''' 抽象类是一种不能被实例化的类 ''' from abc import ABC, abstractmethod class Animal(ABC): ''' 通过装饰器abstractmethod把walk变为抽象方法 ''' @abstractmethod def walk(self): pass def eat(self): print('可以吃') class Dog(Animal): def walk(self): print('dog can walk') class Cat(Animal): def walk(self): print('cat can walk') dog = Dog() dog.walk() # 输出: Woof! cat = Cat() cat.walk() # 输出: Meow!
四、类中内容
实例属性
向实例中添加的数据,可以通过类内 self
或类外实例来添加。
实例方法
第一个参数是 self
的方法,可以通过类内或类外实例来调用。
class Person: def __init__(self, name, sex): self.name = name self.sex = sex def set_name(self, name): self.name = name def __str__(self): return f'类名:{self.name} 类别:{self.sex}' p = Person('泉', '男') print(p)
类属性
类属性可以通过类名直接访问和设置。
class MyClass: # 类属性 class_attribute = 'I am a class attribute' @classmethod def class_method(cls): print(cls.class_attribute) @classmethod def update_class_attribute(cls, new_value): cls.class_attribute = new_value print(MyClass.class_attribute) # I am a class attribute MyClass.class_method() # I am a class attribute
类方法
使用 @classmethod
装饰器的方法,第一个参数通常是 cls
,表示类本身。
class MyClass: @classmethod def class_method(cls): # cls 代表 MyClass 类 print(f"这是 {cls} 的类方法") # 类方法可以通过类直接调用,也可以通过类的实例调用,但推荐通过类直接调用。 MyClass.class_method() # 推荐方式 # 实例也可以调用类方法,但这不是推荐的做法 my_instance = MyClass() my_instance.class_method()
静态方法
使用 @staticmethod
装饰器的方法,没有特殊的参数,通常用于辅助功能。
# 静态方法 不需要 self 或 cls 参数,不能访问类或实例的属性。 class GamePerson: @staticmethod def play_name(): pass @staticmethod def play_age(): pass @staticmethod def play_sex(): pass @staticmethod def play_address(): pass GamePerson.play_name() GamePerson.play_age() GamePerson.play_address() GamePerson.play_sex()
五、Python的灵活性
Python 是一种解释性语言,允许动态地向类中添加内容,包括属性、方法等。
数据的访问级别
在面向对象编程中,数据的访问级别(也称为访问修饰符)用于控制类成员(属性和方法)的可见性和可访问性。Python 没有像 Java 或 C++ 那样的严格访问级别,但是它遵循一些约定来模拟访问控制。以下是 Python 中常见的数据访问级别:
公有(Public)
- 公有属性和方法没有特定的修饰符。它们可以直接从类的实例和类的外部访问。
- 公有成员通常以小写字母开头,这是 Python 的惯例。
# 公有(Public) class Car: def __init__(self, color): self.color = color # 公有属性 def start(self): # 公有方法 return f'The car starts.' print(car.start())
私有(Private)
- 私有成员以双下划线
__
开头。它们通常只在类内部使用,不可从类的外部直接访问。 - Python 中的私有属性和方法实际上并不是真正的私有,它们可以通过
_classname__attributename
的方式访问,但这是一种约定,表明这些成员不应该从外部访问。# 私有(Private) # 私有成员以双下划线 __ 开头。 class Car: def __init__(self): # 私有属性 self.__max_spend = 200 def get_max_spend(self): # 公开私有属性的方法 return self.__max_spend car = Car() print(car.get_max_spend())
保护(Protected)
- 受保护的成员以单个下划线
_
开头。它们可以在类本身和继承的子类中访问,但不应在类的外部访问。 - 与私有成员一样,Python 中的受保护成员也不是真正的受保护,但遵循约定不应从外部访问。
# 私有(Private) # 私有成员以双下划线 __ 开头。 class Car: def __init__(self): # 私有属性 self.__max_spend = 200 def get_max_spend(self): # 公开私有属性的方法 return self.__max_spend
- 受保护的成员以单个下划线
六、属性封装
使用 @property
装饰器可以创建只读属性,使用 @属性名.setter
可以定义设置属性值的方法。
上方的代码保护(Protected)中_color
属性是一个受保护的成员,这意味着它按照惯例应该只在类内部或子类中访问。然而,由于 Python 的动态特性仍可以通过从类的外部访问和修改它。
为了更好地封装,可以使用属性装饰器 @property
来提供对属性的受控访问:
# 保护(Protected) class Car: def __init__(self): self._color = 'red' # 受保护的属性 @property def color(self): # 只读属性,外部不能直接设置_color的值 return self._color @color.setter def color(self, new_color): # 可以在这里添加验证逻辑 self._color = new_color # 使用 setter 方法修改颜色 car = Car() car.color = 'yellow' # 使用属性的方式调用 setter 方法 print(car.color) # 使用属性的方式获取颜色,将输出 'yellow'
在这个修改后的版本中,color
是一个属性,提供了对 _color
的受控访问。外部代码应该使用 car.color
来获取和设置颜色值,而不是直接使用 car._color
。这样,您可以在 color
的 setter 方法中添加验证逻辑,确保属性值的有效性。
单例类
单例类确保只有一个实例存在。它通过控制构造函数来实现这一点。
class Person: pass # 每次调用类都可以生成一个新的实例 p1 = Person() p2 = Person() p3 = Person() print(p1 is p2, p2 is p3, p3 is p1) class Manage(object): instance = None def __new__(cls, *args, **kwargs): ''' 对构造函数进行控制 不是每次都生成新的实例 1. 对类属性instance判断 如果为空 就构造一个实例 并且把实例赋予instance 2. 对类属性instance判断 如果不为空 则直接把他返回 ''' if not Manage.instance: Manage.instance = super().__new__(cls) return Manage.instance def __init__(self): print('初始化函数执行了') m1 = Manage() m2 = Manage() print(m1 is m2, m1 is None, m2 is None)
结论
面向对象编程是一种强大的范式,它通过类和实例的概念,提供了一种自然的方式来模拟现实世界中的事务。Python 的灵活性和动态特性使得OOP在Python中得以广泛应用。通过封装、继承和多态,OOP提高了代码的可重用性、灵活性和可维护性。