目录
4、@property 、@XXX.setter和@XXX.deleter的使用
1、类的基本概念
基本概念:在Python中,类(Class)是创建对象(Object)的蓝图或模板。它定义了对象的属性(Attributes)和方法(Methods)。属性是类中定义的变量,用于存储数据;方法是类中定义的函数,用于执行操作。
1.1、类的定义
类的定义使用关键字class
,后跟类名和类体。类体中可以包含属性(通常作为初始化方法__init__
的参数)和方法(定义为类中的函数)。
class MyClass:
def __init__(self, name, age):
self.name = name
self.age = age
def greet(self):
return f"Hello, my name is {self.name} and I am {self.age} years old."
在这个例子中,MyClass
是一个简单的类,它有两个属性:name
和age
,以及一个方法:greet
。__init__
是一个特殊的方法,称为类的构造函数或初始化方法,用于在创建对象时初始化属性。
1.2、类的使用
要使用类,你需要创建类的实例,这通常通过调用类名并传入必要的参数来完成。
# 创建MyClass的一个实例
person = MyClass(name="Alice", age=30)
# 访问属性
print(person.name) # 输出: Alice
print(person.age) # 输出: 30
# 调用方法
print(person.greet()) # 输出: Hello, my name is Alice and I am 30 years old.
在这个例子中,我们创建了MyClass
的一个实例person
,并通过传入name
和age
参数来初始化它的属性。然后,我们访问了这些属性并调用了greet
方法。
2、类的公有属性和私有属性
类的公有属性:
- 命名:普通属性的定义可以在属性前面加上单下划线_;
- 可见性:公有属性在类的内部和外部都是可见的。这意味着,无论是在类的定义中,还是在类的实例化对象上,公有属性都可以被识别;
- 可访问性:公有属性不仅可见,而且可以在类的内部和外部被直接访问和修改。这是公有属性与私有属性和受保护属性的主要区别之一。
类的私有属性:
- 命名:在属性名和方法名 前面 加上双下划线 __;
- 访问:都不能通过对象直接访问,但是可以在本类内部访问,可以设置方法为外部的访问提供入口;
- 继承:不会被子类继承,子类也无法访问;
- 内部实现原理:Python实际上并没有真正的私有属性。当在类定义中使用双下划线前缀定义属性时,Python会在内部将属性名修改为以类名和两个下划线为前缀的形式(例如,
_ClassName__attributeName
)。然而,这种重命名机制主要是作为一种命名约定来提示开发者不要直接访问这些属性,而不是强制的访问限制。因此,理论上仍然可以通过这种重命名后的属性名来访问私有属性,但这通常是不被推荐的做法。
示例:
class Parent:
_name = 'hello' # 公有属性
def __init__(self, age):
self.__age = age # 私有属性
def __private(self): #私有方法
print('this is private function')
class Child(Parent):
def name(self):
print(f'name: {self._name!r}')
def get_age(self):
# 采用此方法是可以访问父类的私有属性
# return self._Parent__age
# 采用对象直接访问父类的私有属性是无法访问的
return self.__age
def get_private(self):
# 可以通过此方式访问父类的私有方法
self._Parent__private()
child = Child(10)
print(child._Parent__age) # 子对象访问父对象的私有属性
child.name()
child.get_private() # 子对象访问父对象的私有方法
print(child.__dict__) # 查看该对象的属性字典
# child.get_age() # 'Child' object has no attribute '_Child__value'
a = Parent('hello')
a._Parent__private() # 父对象访问自己的私有方法
print(a.__dict__) # 查看该对象的属性字典
2.1、为什么子类不能访问父类的私有属性/方法?
先看下面的示例:
class Parent:
def __init__(self, age):
self.__age = age # 私有属性
class Child(Parent):
def get_age(self):
# 采用此方法是可以访问父类的私有属性
# return self._Parent__age
# 采用对象直接访问父类的私有属性是无法访问的
return self.__age
child = Child(10)
print(child._Parent__age) # 子对象访问父对象的私有属性
child.get_age() # 'Child' object has no attribute '_Child__value'
运行结果如下:
分析:
-
因为python会对类的私有属性做些转换,以保证private字段的私密性。当编译器看到Child.get_value方法要访问私有属性时,它会先把__value变换为_Child_value然后再进行访问,但实际上该私有属性是_Parent__value。所以子类无法访问父类的私有属性,只是因为访问的名称不同。
3、实例方法、类方法、静态方法
实例方法:只有实例对象才能调用的实例方法
类方法:类方法就是Python类的方法,是这个类可以调用的方法,那么参数需要把这个类本身传进去(必须传的,一般写为cls),不需要实例化就可以使用。
静态方法:静态方法是类中的一个独立的普通函数或者说方法,那么参数和普通函数的传参一样,想怎么传就怎么传,类或者实例化的对象都可以直接使用它,也就是说这个静态方法不需要实例化就可以调用。
3.1、实例方法
定义:第一个参数必须是实例对象,该参数名一般约定为“self”,通过它来传递实例的属性和方法(也可以传类的属性和方法)。
调用:只能由实例对象调用。可继承
示例:
# 实例方法
class A():
def __init__(self):
self.name = 'hello'
def test(self):
print('this is test')
A().test() # 只能由实例化对象才能调用
3.2、类方法
定义:使用装饰器@classmethod。第一个参数必须是当前类对象,该参数名一般约定为“cls”,通过它来传递类的属性和方法(不能传实例的属性和方法)
调用:实例对象和类对象都可以调用;可继承
使用场景:
- 工厂方法:类方法可以用作工厂方法,根据输入参数返回类的不同实例。
- 类属性管理:类方法可以用于管理或访问类属性。
示例
以下是一个简单的示例,演示了如何使用类方法来管理一个类属性(计数器),该属性跟踪已创建的类实例数量。
class MyClass:
_instances = 0 # 类属性,用于跟踪实例数量
def __init__(self):
MyClass._instances += 1 # 每次创建实例时,增加计数器
@classmethod
def get_instances_count(cls):
# 类方法,用于获取当前实例数量
return cls._instances
# 创建MyClass的实例
instance1 = MyClass()
instance2 = MyClass()
# 使用类方法获取实例数量
print(MyClass.get_instances_count()) # 输出: 2
3.3、静态方法
定义:使用装饰器@staticmethod。 静态方法是类中的函数,不需要实例。静态方法主要是用来存放逻辑性的代码,逻辑上属于类,但是和类本身没有关系,也就是说在静态方法中,不会涉及到类中的属性和方法的操作。可以理解为,静态方法是个独立的、单纯的函数,它仅仅托管于某个类的名称空间中,便于使用和维护。
示例:
class MathUtilities:
@staticmethod
def add_numbers(a, b):
return a + b
@staticmethod
def multiply_numbers(a, b):
return a * b
# 使用静态方法
print(MathUtilities.add_numbers(5, 10)) # 输出: 15
print(MathUtilities.multiply_numbers(5, 10)) # 输出: 50
4、@property 、@XXX.setter和@XXX.deleter的使用
4.1、@property
作用:
1、让函数可以像普通变量一样使用,将方法转换为相同名称的只读属性
2、创建只读属性,防止属性被修改
3、对要读取的数据进行预处理
示例:
class MyClass:
def __init__(self, value):
self._value = value
@property
def value(self):
"""获取属性值"""
return self._value
obj = MyClass(10)
print(obj.value) # 输出: 10
4.2、@XXX.setter
1、@*.setter装饰器必须在@property装饰器的后面,且两个被修饰的函数的名称必须保持一致,* 即为函数名称。
2、只要两个装饰器成对出现,两个装饰器下的函数名必须相同,以此表示同一个属性的可读可写,如果只有@property出现则表示只读;
3、@*.setter不能单独出现
示例:
class MyClass:
def __init__(self, value):
self._value = value
@property
def value(self):
"""获取属性值"""
return self._value
@value.setter
def value(self, new_value):
"""设置属性值"""
self._value = new_value
obj.value = 20 # 设置新的值
print(obj.value) # 输出: 20
4.3、@XXX.deleter
1、@*.deleter装饰器必须在@property装饰器的后面,且两个被修饰的函数的名称必须保持一致,* 即为函数名称。
2、只要两个装饰器成对出现,两个装饰器下的函数名必须相同,以此表示同一个属性的可读可s删除,如果只有@property出现则表示只读;
3、@*.deleter不能单独出现
示例:
class MyClass:
def __init__(self, value):
self._value = value
@property
def value(self):
"""获取属性值"""
return self._value
@value.setter
def value(self, new_value):
"""设置属性值"""
self._value = new_value
@value.deleter
def value(self):
"""删除属性值"""
del self._value
obj = MyClass(10)
print(obj.value) # 输出: 10
obj.value = 20 # 设置新的值
print(obj.value) # 输出: 20
del obj.value # 删除属性
# 尝试再次访问会导致错误,因为属性已经被删除
# del obj.value # AttributeError: _value