Python面向对象编程:初学者秒懂指南
目录
一、概念和基本属性
二、初始化函数
三、魔法函数
四、构造函数(构造器)与析构函数(类似行为)
构造函数(构造器)__init__
析构函数(类似行为)__del__
五、三大特性:封装、继承、多态
封装
继承
多态
六、抽象类
七、类中的内容以及向类中动态添加内容
1.实例属性
2.实例方法
3.类属性
4.类方法
5.静态方法
八、数据的访问级别
九、属性的封装与操作
十、单例类
一、概念和基本属性
Python面向对象编程(简称OOP)是Python语言中最重要的特性之一。它通过将数据和操作数据的方法封装在对象中,使得程序更加模块化、可重用和易于维护。它遵循一切皆对象的理念,可以将现实世界的事物使用类与实例进行模拟。下面我们来说一说什么是类,什么是实例?
类:类是一个用于创建对象的模板,它约定了对象应该具有的属性和方法。属性是对象的特征(如数据),而方法是对象可以执行的操作(如函数)。类是一个抽象的概念,它本身不表示任何具体的数据,但它可以被用来创建表示具体数据的对象。在Python中,你可以使用class
关键字来定义一个类。类的定义通常包括:
类名:类的唯一标识符 初始化方法( __init__
):一个特殊的方法,用于在创建对象时初始化对象的属性。其他方法:定义在类中的函数,用于执行与对象相关的操作。
实例:实例是类的具体实现,或者说是根据类创建的对象。每个实例都拥有类中定义的属性和方法,但它们可以有不同的属性值。换句话说,实例是类的具体实例化结果,它代表了具体的数据和与之相关的操作。在Python中,你可以通过类名后跟一对圆括号(可能包含初始化参数)来创建类的实例。在创建实例时,Python会自动调用类的__init__
方法来初始化对象的属性。
下面是代码示例:
# 自定义一个类
class MyClass:
pass
# 声明一个MyClass类的实例
mc1 = MyClass()
二、初始化函数
在Python中,初始化函数是一个特殊的方法,用于在创建类的新实例时设置或初始化对象的属性。这个方法通常被称为__init__
方法(双下划线init,后跟双下划线)。__init__
方法是类的构造器,它在创建类的新实例时自动调用。
__init__
方法的基本用法:
定义:在类定义中,
__init__
方法被定义为一个方法,但它不接受self以外的任何参数,除非这些参数是你希望在创建对象时指定的。self 参数:
__init__
方法必须包含一个名为self
的参数。在类的方法中,self
代表类的实例本身。通过这个参数,你可以访问类中的属性和其他方法。初始化属性:在
__init__
方法内部,你可以设置对象的属性。这通常通过self.属性名 = 值
的形式来完成。
下面是代码示例:
class Person:
def __init__(self, name, age):
self.name = name # 初始化name属性
self.age = age # 初始化age属性
def greet(self):
print(f"name:{self.name},age:{self.age}")
# 创建Person类的实例
person1 = Person("小明", 18)
# 调用实例的方法
person1.greet() # 输出: name:小明,age:18
在这个示例中,Person
类有一个 __init__
方法,它接受三个参数:self
、name
和 age
。self
参数是对新创建的 Person
实例的引用,而 name
和 age
是传递给 __init__
方法的参数,用于初始化对象的属性。
当 Person("小明", 18)
被执行时,Python 创建了一个 Person
类的实例,并自动调用 __init__
方法,将 "小明"
和 18
作为参数传递给它。然后,__init__
方法将这些值分别赋给新实例的 name
和 age
属性。
三、魔法函数
Python中的魔法函数(又称为特殊方法或双下划线方法)是Python类定义中可以重写的特殊方法。这些方法总是以双下划线开始和结束,例如__init__
、__str__
、__add__
等。这些方法提供了对象的内置行为,如初始化、字符串表示、算术运算等。当特定的操作被应用到类的实例时,Python解释器会自动调用这些方法。下面是一些常用的魔法函数及其用途的简要介绍:
__init__(self, ...)
:类的初始化方法。当创建类的新实例时,会自动调用此方法。它通常用于设置对象的初始状态或属性。
__str__(self)
:定义对象的字符串表示形式。当对对象使用str()
函数或者print()
函数打印对象时,会自动调用此方法。
__repr__(self)
:定义对象的“官方”字符串表示形式,并应尽可能提供足够的信息,以便能够通过eval()
函数重新创建对象。在交互式环境中,如果__str__
没有被定义,则会使用__repr__
的输出。
__add__(self, other)
:定义加法运算符(+
)的行为。
__sub__(self, other)
:定义减法运算符(-
)的行为。
__mul__(self, other)
:定义乘法运算符(*
)的行为。
__truediv__(self, other)
:定义真除法运算符(/
)的行为(Python 3)。
__floordiv__(self, other)
:定义地板除运算符(//
)的行为。
__mod__(self, other)
:定义取模运算符(%
)的行为。
__pow__(self, power, modulo=None)
:定义幂运算符(**
)的行为,以及可选的模幂运算(pow(x, y, z)
)。
__eq__(self, other)
:定义等于运算符(==
)的行为。
__ne__(self, other)
:定义不等于运算符(!=
)的行为。
__lt__(self, other)
:定义小于运算符(<
)的行为。
__le__(self, other)
:定义小于等于运算符(<=
)的行为。
__gt__(self, other)
:定义大于运算符(>
)的行为。
__ge__(self, other)
:定义大于等于运算符(>=
)的行为。
__getitem__(self, key)
:定义获取容器中指定元素的行为(如使用obj[key]
)。
__setitem__(self, key, value)
:定义设置容器中指定元素的值的行为(如使用obj[key] = value
)。
__delitem__(self, key)
:定义删除容器中指定元素的行为(如使用del obj[key]
)。
__iter__(self)
:定义容器迭代的行为。
__next__(self)
:与迭代器协议相关,定义迭代器对象的next()
方法的行为。
下面是代码示例:
class MyClass:
def __init__(self, name, age):
print(f"初始化函数执行了")
self.name = name
self.age = age
def __str__(self):
return f"醒醒啦:{self.name}"
def __len__(self):
return len(self.name)
def __gt__(self, other):
return self.age > other.age
def __lt__(self, other):
return self.age < other.age
def __ge__(self, other):
return self.age >= other.age
def __le__(self, other):
return self.age <= other.age
def __eq__(self, other):
return self.age == other.age and self.name == other.name
def __ne__(self, other):
return self.age != other.age or self.name != other.name
def __add__(self, other):
return self.age + other.age
def __sub__(self, other):
return self.age - other.age
def __mul__(self, other):
return self.age * other.age
def __divmod__(self, other):
return divmod(self.age, other.age)
def __mod__(self, other):
return self.age % other.age
def __truediv__(self, other):
return self.age / other.age
def __floordiv__(self, other):
return self.age // other.age
mc = MyClass("张飞", 30)
print(mc) # 初始化函数执行了 醒醒啦:张飞
print(len(mc)) # 2
mc2 = MyClass("孙尚香", 18)
print(mc != mc2, mc == mc2) # 初始化函数执行了 True False
print(mc + mc2, mc - mc2, mc * mc2, mc % mc2, divmod(mc, mc2), mc / mc2, mc // mc2)
# 48 12 540 12 (1, 12) 1.6666666666666667 1
四、构造函数(构造器)与析构函数(类似行为)
在Python中,虽然术语“构造函数”和“析构函数”经常用于描述某些特殊方法的行为,但严格来说,Python使用的是__init__
作为构造器而不是构造函数,并且Python并没有直接称为“析构函数”的特殊方法,而是使用__del__
方法作为类似析构的行为。
构造函数(构造器)__init__
目的: __init__
方法在对象被创建后立即调用,用于初始化新创建的对象的状态或属性。语法: def __init__(self, [param1, param2, ...]):
用法: __init__
方法不是可选的,但如果你不需要进行初始化,可以简单地让它在没有参数的情况下返回。通常,你会使用这个方法来设置对象的初始状态。注意: __init__
不是构造函数,因为它不返回任何值(即返回None
),且它用于设置对象状态,而不是返回对象实例(因为实例的创建是在__init__
调用之前由__new__
方法完成的,尽管__new__
方法很少需要重写)。
析构函数(类似行为)__del__
目的: __del__
方法在对象被销毁之前调用,可以用来执行清理操作,比如释放对象占用的资源。语法: def __del__(self):
用法:虽然 __del__
可以用于执行清理工作,但通常不推荐依赖它来管理资源,因为Python的垃圾收集器会在对象不再被需要时自动销毁对象,而这个时间点是不可预测的。相反,推荐使用上下文管理器(with
语句)或显式关闭资源(如文件或数据库连接)的方法来管理资源。注意:如果 __del__
方法抛出异常,并且没有被捕获,那么它将导致程序崩溃。因此,在__del__
方法中执行的代码应该非常小心,确保它不会引发异常。
下面是代码示例:
class MyClass:
def __new__(cls, *args, **kwargs):
# 调用父类的new方法创建一个实例
instance = super().__new__(cls)
print(f"构造函数执行了", id(instance)) # 构造函数执行了 2996077860624
# 将创建好的实例返回 返回给初始化函数
return instance
def __init__(self, name):
print(f"初始化函数执行了", id(self)) # 初始化函数执行了 2996077860624
self.name = name
def __del__(self):
print(f"析构函数执行了")
mc1 = MyClass("阿拉伯")
print(id(mc1), id(None), mc1 is None) # 2996077860624 140736623045832 False
mc1 = None
print("程序执行完毕,将要退出") # 析构函数执行了 程序执行完毕,将要退出
# 程序退出执行析构mc1
这段代码的执行顺序:
先通过__new__
创建实例,打印出“构造函数执行了”和instance的id
然后通过__init__
初始化实例,打印出“初始化函数执行了”和self的id
接着使用实例,最后(在程序结束时)通过垃圾回收机制调用__del__
销毁实例。执行析构函数,并打印出“程序执行完毕,将要退出”。在这里将示例mc1设置为了None,目的是告诉析构函数,这个实例不再使用了,所以析构函数会将其进行销毁处理。
但是,需要注意的是,__del__
的调用时机是不确定的,它依赖于Python的垃圾回收机制。
五、三大特性:封装、继承、多态
封装
封装简单来说就是将一系列的数据以及这些数据相关的操作放在一个封闭的空间内,可以有效保证数据的安全性,可以通过定义属性的类型来对数据进行相应的访问隔离(下面会讲到,这里只阐述简单的封装。)
下面是代码示例:
class Light:
def __init__(self):
self.state = False
self.colors = ["红色", "绿色", "蓝色"]
self.current = 0
def is_open(self):
return self.state
def change_state(self):
self.state = not self.state
def get_color(self):
return self.colors[self.current]
def set_color(self):
self.current += 1
if self.current == len(self.colors):
self.current = 0
# 创建实例
l0 = Light()
# 打印灯光初始状态
print(l0.is_open()) # False
# 切换灯光状态并打印灯光新状态
l0.change_state()
print(l0.is_open()) # True
l0.change_state()
print(l0.is_open()) # False
# 打印灯光初始颜色
print(l0.get_color()) # 红色
# 切换灯光颜色并打印灯光新颜色
l0.set_color()
print(l0.get_color()) # 绿色
l0.set_color()
print(l0.get_color()) # 蓝色
l0.set_color()
print(l0.get_color()) # 红色
这段代码定义了一个Light类,用于展示灯的各种状态,封装了初始函数__init__,is_open方法、change_state方法,get_color和set_color方法,初始函数中封装了灯的状态state、灯的颜色,并且初始化了当前状态current_state为False(关闭状态)。
继承
继承是面向对象编程中代码重用的一个重要机制。它允许创建一个类(称为子类或派生类)来继承另一个类(称为父类或基类)的属性和方法。子类可以继承父类的所有公共属性和方法,也可以添加新的属性和方法或覆盖(重写)继承的方法。
Python中的继承分为单继承和多继承两大类。我们先介绍单继承,单继承就是子类只继承一个父类,方法是在子类的初始化函数__init__时对单个父类的初始化函数进行继承,具体的格式为:super().__init__(要继承父类的属性)。
下面是代码示例:
class Person(object):
def __str__(self):
return f"人"
class SuperPerson(Person):
# pass
def __str__(self):
return f"超人"
# 实例化这两个类
p1 = Person()
sp1 = SuperPerson()
# 打印这两个类,它们中都有__str__魔法函数,可以直接打印
print(p1)
print(sp1)
# 打印这两个实例的类型
print(p1.__class__)
print(sp1.__class__)
# 打印两个类的父类
print(Person.__base__)
print(SuperPerson.__base__)
# 类的父类不只一个,打印类的父类元组
print(Person.__bases__)
print(SuperPerson.__bases__)
# 如果将SuperPerson类的__str__注释掉
# 在保证类的完整性的前提下(pass),执行print(sp1)会返回"人"
# 这是因为其会去去父类中寻找__str__直到找到为止
这段代码定义了两个类:Person和SuperPerson,SuperPerson继承Person,即Person是SuperPerson的父类,子类可以继承父类的属性和方法,Python中的所有类的默认父类都为object类。
子类可以继承父类的方法,子类除了继承父类的方法外,还可以有自己的方法或形参变量,也可以对父类的属性或者方法进行重写。
下面是代码示例:
class Person(object):
def __init__(self, name):
self.name = name
def __str__(self):
return f"名字:{self.name}"
def walk(self):
print("行走")
class SuperPerson(Person):
# 子类对父类的初始化函数进行调用
# 在这里添加了子类独有的形参变量skill
def __init__(self, name, skill):
# 想让子类独有,无需传入父类
super().__init__(name)
# 在子类内部独自声明这个skill参数
self.skill = skill
# 子类对父类的str函数进行调用
def __str__(self):
# 在这里首先返回继承父类的str , 再打印了子类独有的形参变量
return f"{super().__str__()} 技能:{self.skill}"
def fly(self):
print("飞行")
p1 = Person("张飞")
print(p1)
# Person类中声明了这个函数,所以Person的实例化对象p1可以调用
p1.walk()
sp1 = SuperPerson("西施", "龙珠为契")
print(sp1)
# SuperPerson类中没有声明walk函数,但是它会去父类中找,sp1照样可以调用
sp1.walk()
# fly函数为SuperPerson类中独有的函数,sp1可以调用,但是父类的实例化对象无法调用
sp1.fly()
在这段代码中,SuperPerson类除了继承Person的属性和方法外,还添加了自己独有的属性skill,这个skill属性父类无法访问,因为这是独立定义在子类中的,父类对子类并没有继承说法。
接下来我们来介绍多继承,多继承就是一个子类继承多个父类,具体方法是在子类定义初始化函数__init__时对多个父类的初始化函数进行继承,具体格式为:多继承:类名.__init__(self, *args),一定要写self,确保子类与父类的self是同一个。
下面是代码示例:
class MoveAble:
def __init__(self, speed):
self.speed = speed
def move(self):
print(f"i can move, my speed is {self.speed}")
def __str__(self):
return f"speed:{self.speed}"
class SpeakAble:
def __init__(self, language):
self.language = language
def speak(self):
print(f"i can speak {self.language}")
def __str__(self):
return f"language:{self.language}"
class AttackAble:
def __init__(self, skill):
self.skill = skill
def __str__(self):
return f"skill:{self.skill}"
class Person(MoveAble, SpeakAble):
def __init__(self, name, speed, language):
# 多继承:类名.__init__(self, *args)
# 一定要写self,子类与父类的self是同一个
MoveAble.__init__(self, speed)
SpeakAble.__init__(self, language)
self.name = name
def show(self):
print(f"my name is {self.name}")
def __str__(self):
# 多继承:类名__str__(self)
return f"name:{self.name}\t{MoveAble.__str__(self)}\t{SpeakAble.__str__(self)}"
class AutoMan(Person, AttackAble):
def __init__(self, name, speed, language, skill):
Person.__init__(self, name, speed, language)
AttackAble.__init__(self, skill)
def __str__(self):
# 多继承:类名__str__(self)
return f"{Person.__str__(self)}\t{AttackAble.__str__(self)}"
p0 = Person("小王", 100, "English")
print(p0) # name:小王 speed:100 language:English
p0.show() # my name is 小王
p0.move() # i can move, my speed is 100
p0.speak() # i can speak English
a1 = AutoMan("塞罗", 1000, "光之语言", "塞罗光线")
print(a1) # name:塞罗 speed:1000 language:光之语言 skill:塞罗光线
上方代码中,类Person对类MoveAble和SpeakAble进行多继承,类AutoMan对Person和AttackAble进行多继承,获取他们的方法与属性,从而减少代码冗余。
多态
Python中的多态通常通过定义特定的参数和重写父类方法来实现。
通过特定参数实现多态:
class Students:
def student_age(self, *args):
print(args)
c1 = Students()
c1.student_age(11)
c1.student_age(11, 12)
c1.student_age(11, 12, 13, 14)
通过方法重写实现多态:
class Amail:
def walk(self, speed):
print(f"走")
class Dog(Amail):
def walk(self, speed):
print(f"摇尾巴")
class Cat(Amail):
def walk(self, speed):
print(f"到处窜")
六、抽象类
Python中的抽象类是一种特殊的类,它主要用于定义一组相关类的共同特性和行为,但自身不能被实例化。它的特点如下:
- 不可实例化:抽象类不能被直接实例化,只能作为其他类的基类使用,强制子类实现特定的方法。
- 包含抽象方法:抽象类中可以包含抽象方法,这些方法是没有具体实现的,子类必须实现这些方法才能被实例化。抽象方法通常使用
@abstractmethod
装饰器进行标记。- 可包含非抽象方法和属性:除了抽象方法外,抽象类还可以包含非抽象方法和属性,这些方法和属性在子类中可以直接使用或重写。
- 使用
abc
模块:在Python中,定义抽象类通常需要用到abc
(Abstract Base Classes)模块,该模块提供了ABC
基类和@abstractmethod
装饰器等工具。
下面是代码示例:
"""
抽象类:不直接实例化,通过子类来产生实例
继承抽象类的子类必须实现所有抽象方法
"""
from abc import ABC, abstractmethod
class Animal(ABC):
# 用来定义抽象方法的装饰器
@abstractmethod
def walk(self):
pass
def eat(self):
print(f"可以吃")
class Dog(Animal):
def walk(self):
print(f"摇摇尾巴")
class Cat(Animal):
def walk(self):
print("跳来跳去")
# Animal是抽象类,不能直接实例化,下面尝试对Animal进行实例化会报错
try:
a1 = Animal()
a1.walk()
except Exception as e:
print(e) # Can't instantiate abstract class Animal with abstract method walk
d0 = Dog()
d0.walk() # 摇摇尾巴
d0.eat() # 可以吃
# isinstance(d0, Animal)为True,说明通过子类Dog产生了Animal的实例
# 即d0既是Dog的实例,也是Animal的实例
print(isinstance(d0, Dog), isinstance(d0, Animal)) # True True
c0 = Cat()
c0.walk() # 跳来跳去
c0.eat() # 可以吃
print(isinstance(c0, Cat), isinstance(c0, Cat)) # True True
七、类中的内容以及向类中动态添加内容
类中的内容分为五类:实例属性,实例方法,类属性,类方法,静态方法。
1.实例属性
实例属性是绑定到类实例上的变量。每个实例都有自己独立的实例属性副本,即使这些实例属于同一个类。实例属性通常在实例的__init__
方法(构造函数)中定义和初始化。
2.实例方法
实例方法是定义在类中的函数,它们需要至少一个参数(通常是self
),这个参数是对类实例自身的引用。实例方法可以访问和修改实例属性。
3.类属性
类属性是绑定到类本身的变量,而不是类的实例。所有类的实例都共享同一个类属性。类属性在类定义中直接定义,不通过任何方法。
4.类方法
类方法是通过@classmethod
装饰器定义的,它们至少需要一个参数(通常是cls
),这个参数是对类自身的引用,而不是类的实例。类方法用于修改类属性或执行与类相关的操作,但不修改实例的状态。
5.静态方法
静态方法是通过@staticmethod
装饰器定义的,它们既不接收隐式的self
参数(实例的引用),也不接收cls
参数(类的引用)。静态方法本质上是普通函数,只是被放在了类的命名空间中。
下面是对类内容介绍的代码示例:
class Person:
# 类属性:在类中定义的属性,类属性是绑定到类上的,每个实例共享同一个类属性
# 类属性通过类名访问,通过类名修改
MAX_AGE = 120
MIN_AGE = 0
# 静态方法:没有特殊的形参,有装饰器,可以直接通过类名调用
@staticmethod
def my_fun1(a, b):
return a if a > b else b
# 类方法:在类中定义的方法,类方法第一个参数是cls,则它是类方法,有装饰器
# 和类属性一样,通过类名调用,也可通过实例名调用但不推荐
@classmethod
def info(cls):
print(cls)
def __init__(self, name, age):
# 向实例中添加的属性 称为实例属性
self.name = name
self.age = age
def set_name(self, name):
# 第一个参数是self,则它是实例方法
self.name = name
def get_name(self):
return self.name
def __str__(self):
return f"名字:{self.get_name()} 年纪:{self.age}"
# 实例属性:在类中定义的属性,实例属性是绑定到实例上的,每个实例都有自己独立的属性
# 实例方法:在类中定义的方法,实例方法第一个参数是self,则它是实例方法
p0 = Person("小明", 18)
print(p0.name, p0.age)
p0.set_name("小路")
print(p0.get_name())
# 类属性通过类名访问
print(Person.MAX_AGE, Person.MIN_AGE)
# 类属性通过类名修改
Person.MAX_AGE = 130
print(Person.MAX_AGE, Person.MIN_AGE)
# 类属性也可以通过实例名访问,但是不推荐
print(p0.MAX_AGE, p0.MIN_AGE)
# 类属性通过实例名修改,相当于向实例中添加了一个MAX_AGE属性,而不是修改Person类中的MAX_AGE属性
p0.MAX_AGE = 140
print(p0.MAX_AGE, Person.MAX_AGE)
# 直接通过类名调用
print(Person.my_fun1(10, 20))
在Python中,我们还可以动态添加类内容,下面是动态添加类内容的代码示例:
import types
class Student:
pass
# 实例化两个实例s1和s2
s1 = Student()
s2 = Student()
# 向实例s1中动态添加实例属性
# 每个实例都有自己的实例属性,不共享
s1.name = "小明"
# 实例s1可以访问
print(s1.name)
# 实例s2不可以访问
try:
print(s2.name)
except AttributeError as e:
print(e)
# 类不可以访问
try:
print(Student.name)
except AttributeError as e:
print(e)
# 向实例s1中动态添加实例方法
# 实例方法绑定在实例s1中
def my_set_name(self, name):
self.name = name
return name
# 添加实例方法需要用到types模块下的MethodType方法,该方法接受两个参数:要添加的方法名和实例名
# 实例调用方法时调用的方法名是添加实例方法时等号前面的
s1.set_name = types.MethodType(my_set_name, s1)
# 实例s1可以访问
print(s1.set_name("小红"))
# 实例s2不可以访问
try:
print(s2.set_name("小红"))
except AttributeError as e:
print(e)
# 类Student不可以访问
try:
print(Student.set_name("小红"))
except AttributeError as e:
print(e)
# 向类中动态添加类属性
Student.name = "小王"
# 类可以直接访问
print(Student.name)
# 实例s1,s2也可以访问(不推荐)
# 上方为s1添加了实例属性,就近原则,执行添加的实例属性
# 通过实例访问 name 属性时,Python 会首先查找实例属性,如果没有找到,才会查找类属性。
# 这就是所谓的“属性查找的最近原则”。
print(s1.name)
print(s2.name)
# 向类中动态添加类方法,通常将cls设置为类方法的默认参数名
# 方法一:
@classmethod
def my_info(cls):
print("我是类方法")
Student.info = my_info
# 类可以访问
Student.info()
# 实例s1,s2也都可以访问(不推荐)
s1.info()
s2.info()
# 方法二:
# def my_info(cls):
# print("我是类方法")
#
#
# Student.info = classmethod(my_info)
# 向类中动态添加静态方法
@staticmethod
def my_fun(a, b):
return a if a > b else b
Student.fun = my_fun
# 类可以访问
print(Student.fun(10, 20))
# 实例s1,s2也都可以访问(不推荐)
print(s1.fun(30, 40))
print(s2.fun(50, 60))
八、数据的访问级别
Python中的数据访问级别分为三大类:公有、私有以及保护类型。
公有类型(public):普通名字,在类内和类外和子类中都可以使用
私有类型(private):__开头的名字,只能在类内访问
保护类型(protect):_开头,可以在类外可以强制访问(不推荐),保护是为子类服务的
下面是代码示例:
class Person:
def __init__(self, name, __age, _sex):
self.name = name
self.__age = __age
self._sex = _sex
def get_sex(self):
return self._sex
def set_sex(self, _sex):
self._sex = _sex
def get_age(self):
return self.__age
def set_age(self, __age):
self.__age = __age
def get_name(self):
return self.name
def set_name(self, name):
self.name = name
def __str__(self):
return f"name:{self.get_name()},age:{self.get_age()},sex:{self.get_sex()}"
p0 = Person("张飞", 20, "男")
# 公有数据,私有数据,保护数据都可以在类内访问
print(p0)
# 公有数据在类外可以访问
print(p0.name, p0.get_name())
p0.set_name("李华")
print(p0.name)
# 私有数据在类外不可以访问
try:
print(p0.__age)
except AttributeError as e:
print(e)
# 但是可以通过定义公有函数来访问私有数据
print(p0.get_age())
# 保护数据在类外可以强制访问(不推荐)
print(p0._sex, p0.get_sex())
p0.set_sex("女")
print(p0._sex)
class SuperPerson(Person):
def __str__(self):
return f"性别:{self._sex}"
sp1 = SuperPerson("绿巨人", 30, "男")
# 公有数据可以在子类的类外访问
print(sp1.name, sp1.get_name())
# 保护数据可以在子类的类外访问
print(sp1._sex)
# 保护数据可以在子类的类外访问
print(sp1)
设置数据访问类型的主要好处包括:
- 隐藏内部实现:通过将数据和方法封装在类中,可以隐藏对象内部的数据结构和方法的实现细节,只暴露必要的接口给外部。
- 提高安全性:通过控制对类成员的访问(例如,只允许通过特定的方法进行访问),可以防止外部代码错误地修改或访问内部状态。
- 灵活性和可扩展性:封装使得在不修改现有代码的基础上增加新的功能或修改内部实现成为可能,因为外部代码只与类的接口交互。
九、属性的封装与操作
在Python中可以使用property
装饰器来封装类的属性,可以通过自定义的get
和set
方法来控制属性的访问和修改。
下面是属性封装的代码实例:
class Person:
def __init__(self, __name, age, __sex, __height):
self.__name = __name
self.age = age
self.__sex = __sex
self.__height = __height
@property
def height(self):
return self.__height
@height.setter
def height(self, height):
self.__height = height
def set_sex(self, sex):
if sex in ["男", "女"]:
self.__sex = sex
else:
print("设置失败")
def get_sex(self):
return self.__sex
# 使用property封装真正的属性
sex = property(get_sex, set_sex)
def get_name(self):
return self.__name
def set_name(self, name):
if 2 <= len(name) <= 4:
self.__name = name
else:
print(f"设置失败")
p = Person("张飞", 20, "男", 180)
# 获取和设置age 直接使用 但容易产生不合法数据
print(p.age)
# 年龄不合法,但还是设置成功了
p.age = -30
print(p.age)
# 获取和设置name 需要通过调用函数获取 但数据安全(可有效规避不合法数据)
print(p.get_name())
# 姓名不符合函数内的条件,提示设置失败
p.set_name("")
print(p.get_name())
p.set_name("张飞飞")
print(p.get_name())
# property设置之后可以更方便的获取,不需要再通过函数
print(p.sex)
p.sex = "保密"
print(p.sex)
# 加入装饰器也可以实现使它更方便的被获取
print(p.height)
p.height = 190
print(p.height)
Python中还提供了一系列的属性操作方法,可以通过这些操作对属性进行判断、获取、改变、删除的操作,使用方法如下所示:
hasattr(对象,"属性名")----返回布尔值 getattr(对象,"属性名")----返回属性值 setattr(对象,"属性名",属性值) delatter(对象,"属性名")----返回布尔值
代码示例如下所示:
class Person:
pass
p0 = Person()
# 动态添加实例属性
p0.name = "小王"
# 判断对象中有没有属性
print(hasattr(p0, "name"), hasattr(Person, "name"))
# 获取对象的属性值
print(getattr(p0, "name"))
# 修改对象的属性
setattr(p0, "name", "小路")
print(p0.name)
# 删除对象的属性
delattr(p0, "name")
if not hasattr(p0, "name"):
print("属性name删除成功")
十、单例类
在Python中,单例类是一种设计模式,其核心思想是确保一个类仅有一个实例,并提供一个全局访问点来获取这个实例。单例模式的主要用途包括控制资源访问、减少系统开销、状态共享等。
下面是代码示例:
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):
"""
初始化函数 初始化实例 向self中添加内容
"""
print(f"初始化函数执行了")
m1 = Manage()
m2 = Manage()
print(m1 is m2, m1 is None, m2 is None)
单例类的适用场景:
系统中某些类的实例只能有一个,如日志记录器、配置管理器等。 需要实现状态共享的场景,如连接池、缓存等。 频繁创建和销毁实例会带来较大开销的场景,使用单例可以减少这些开销。
作者:小王学编程.exe