类特殊成员(属性与方法)

发布时间 2023-12-13 18:09:18作者: 善战者求之于势

1. setattr()、getattr()、hasattr()

setattr()getattr()hasattr() 是 Python 中用于处理对象属性的三个内建函数。

setattr()

setattr() 函数用于设置对象的属性值。其基本语法为:

setattr(object, attribute, value)
  • object 是要设置属性的对象。
  • attribute 是属性的名称。
  • value 是要设置的属性值。
class Person:
    pass

# 创建 Person 类的实例
person_instance = Person()

# 设置实例的属性值
setattr(person_instance, 'name', 'Alice')
setattr(person_instance, 'age', 25)

print(person_instance.name)  # 输出: Alice
print(person_instance.age)   # 输出: 25

getattr()

getattr() 函数用于获取对象的属性值。其基本语法为:

getattr(object, attribute[, default])
  • object 是要获取属性的对象。
  • attribute 是属性的名称。
  • default 是可选参数,用于指定在属性不存在时的默认值。
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
# 创建 Person 类的实例
person_instance = Person(name='Alice', age=25)

# 获取实例的属性值
name_value = getattr(person_instance, 'name')
age_value = getattr(person_instance, 'age')

print(name_value)  # 输出: Alice
print(age_value)   # 输出: 25

# 获取不存在的属性,提供默认值
gender_value = getattr(person_instance, 'gender', 'Unknown')
print(gender_value)  # 输出: Unknown

hasattr()

hasattr() 函数用于检查对象是否具有指定的属性。其基本语法为:

hasattr(object, attribute)
  • object 是要检查属性的对象。
  • attribute 是要检查的属性名称。
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

# 创建 Person 类的实例
person_instance = Person(name='Alice', age=25)

# 检查实例是否具有指定的属性
has_name = hasattr(person_instance, 'name')
has_gender = hasattr(person_instance, 'gender')

print(has_name)    # 输出: True
print(has_gender)  # 输出: False

这三个函数通常用于动态地操作对象的属性,特别是当属性的名称是在运行时确定的情况下。

2. init():初始化对象的实例

__init__() 是 Python 类中的一个特殊方法,用于初始化对象的实例。它是一个构造方法(constructor),在创建类的实例时自动被调用。__init__() 方法允许你在创建对象时执行一些必要的设置或初始化工作。

以下是 __init__() 方法的基本用法:

class MyClass:
    def __init__(self, parameter1, parameter2, ...):
        # 在这里进行初始化工作
        self.attribute1 = parameter1
        self.attribute2 = parameter2
        # ...

# 创建类的实例
my_instance = MyClass(value1, value2, ...)

示例:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

# 创建 Person 类的实例
person_instance = Person("Alice", 25)

# 访问实例属性
print(person_instance.name)  # 输出: Alice
print(person_instance.age)   # 输出: 25

在这个例子中,__init__() 方法用于初始化 Person 类的实例,分别设置了 nameage 属性。

3. new():init()能力补充

new() 是一种负责创建类实例的静态方法,它无需使用 staticmethod 装饰器修饰,且该方法会优先 init() 初始化方法被调用。

那么,什么情况下使用 new() 呢?答案很简单,在 init() 不够用的时候。

例如,前面例子中对 Python 不可变的内置类型(如 int、str、float 等)进行了子类化,这是因为一旦创建了这样不可变的对象实例,就无法在 init() 方法中对其进行修改。

有些读者可能会认为,new() 对执行重要的对象初始化很有用,如果用户忘记使用 super(),可能会漏掉这一初始化。虽然这听上去很合理,但有一个主要的缺点,即如果使用这样的方法,那么即便初始化过程已经是预期的行为,程序员明确跳过初始化步骤也会变得更加困难。不仅如此,它还破坏了“init() 中执行所有初始化工作”的潜规则。

注意,由于 new() 不限于返回同一个类的实例,所以很容易被滥用,不负责任地使用这种方法可能会对代码有害,所以要谨慎使用。一般来说,对于特定问题,最好搜索其他可用的解决方案,最好不要影响对象的创建过程,使其违背程序员的预期。比如说,前面提到的覆写不可变类型初始化的例子,完全可以用工厂方法(一种设计模式)来替代。

4. repr():输出对象内部信息

我们经常会直接输出类的实例化对象,例如:

class CLanguage:    
    pass
clangs = CLanguage()
print(clangs)

程序运行结果为:

<__main__.CLanguage object at 0x000001A7275221D0>

通常情况下,直接输出某个实例化对象,本意往往是想了解该对象的基本信息,例如该对象有哪些属性,它们的值各是多少等等。但默认情况下,我们得到的信息只会是“类名+object at+内存地址”,对我们了解该实例化对象帮助不大。

那么,有没有可能自定义输出实例化对象时的信息呢?答案是肯定,通过重写类的 repr() 方法即可。事实上,当我们输出某个实例化对象时,其调用的就是该对象的 repr() 方法,输出的是该方法的返回值。

通过在类中重写这个方法,从而实现当输出实例化对象时,输出我们想要的信息。

案例配置:

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return f"Point(x={self.x}, y={self.y})"

# 创建 Point 类的实例
point = Point(1, 2)

# 使用 repr() 函数获取对象的字符串表示
repr_str = repr(point)
print(repr_str)  # 输出: Point(x=1, y=2)

# 直接打印对象,会调用对象的 __repr__() 方法
print(point)     # 输出: Point(x=1, y=2)

上面案例通过__repr__输出对象中属性的一些信息,可以利用这些数值返回了解数据的信息。

5. str():输出提示信息

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return f"Point at ({self.x}, {self.y})"

# 创建两个 Point 类的实例
point1 = Point(3, 5)
point2 = Point(7, 10)

# 调用 __str__() 方法
str_representation1 = str(point1)
str_representation2 = str(point2)

# 直接使用 print() 函数,会自动调用 __str__() 方法
print(point1)  # 输出: Point at (3, 5)
print(point2)  # 输出: Point at (7, 10)

# 使用 f-string 输出字符串表示
print(f"The first point is {point1}.")  # 输出: The first point is Point at (3, 5).
print(f"The second point is {point2}.")  # 输出: The second point is Point at (7, 10).

__str__ 方法的目的是为了在使用 print() 函数时提供一个友好、人类可读的字符串表示形式。如果没有定义 __str__ 方法,那么 print() 函数会调用对象的 __repr__ 方法,或者使用默认的输出方式。

6. issubclass和isinstance函数

  • issubclass(cls, class_or_tuple):检查 cls 是否为后一个类或元组包含的多个类中任意类的子类。
  • isinstance(obj, class_or_tuple):检查 obj 是否为后一个类或元组包含的多个类中任意类的对象。

issubclass 的应用场景

(1)检查继承关系: 在编写框架或库时,可以使用 issubclass 来检查一个类是否是某个基类的子类,以确保符合预期的继承关系。

class Shape:
    pass

class Circle(Shape):
    pass

# 在某个函数中检查是否是 Shape 的子类
def process_shape(shape_class):
    if issubclass(shape_class, Shape):
        # 处理 Shape 类或其子类的逻辑
        pass

(2)动态选择实现: 在某些情况下,根据对象的类型来选择不同的实现。issubclass 可以帮助你动态选择适当的类。

class Shape:
    def draw(self):
        raise NotImplementedError

class Circle(Shape):
    def draw(self):
        print("Drawing a circle")

class Square(Shape):
    def draw(self):
        print("Drawing a square")

def draw_shape(shape):
    if issubclass(type(shape), Shape):
        shape.draw()

isinstance 的应用场景

(1)类型检查: 在函数中对传入的参数进行类型检查,确保参数是预期的类或类型。

def process_data(data):
    if isinstance(data, (list, tuple)):
        # 处理列表或元组的逻辑
        pass

(2)多态实现: 允许对象根据其类型表现出不同的行为。这在设计模式中的多态概念中经常用到。

class Animal:
    def make_sound(self):
        pass

class Dog(Animal):
    def make_sound(self):
        print("Woof!")

class Cat(Animal):
    def make_sound(self):
        print("Meow!")

def animal_sound(animal_instance):
    if isinstance(animal_instance, Animal):
        animal_instance.make_sound()

(3)处理不同版本或类型的对象: 在处理对象集合时,可以使用 isinstance 来过滤或处理不同版本或类型的对象。

class Version1:
    pass

class Version2:
    pass

objects = [Version1(), Version2()]

for obj in objects:
    if isinstance(obj, Version1):
        # 处理 Version1 类型的对象
        pass
    elif isinstance(obj, Version2):
        # 处理 Version2 类型的对象
        pass

总体来说,这两个函数在处理多态性、动态类型和对象关系时非常有用。在编写灵活且可扩展的代码时,它们可以帮助你进行更动态的类型检查。

7. 生成器:按需生成值非一次性保存在内存

生成器(Generator)是 Python 中用于生成迭代器的一种特殊类型的对象。它允许你按需生成一系列值,而不是一次性生成并保存在内存中。生成器使用 yield 语句来产生值,而不是使用 return

def fibonacci_generator(n):
    a, b = 0, 1
    count = 0
    while count < n:
        yield a
        a, b = b, a + b
        count += 1

# 使用生成器获取斐波那契数列的前10个数字
fibonacci_gen = fibonacci_generator(10)

for number in fibonacci_gen:
    print(number)

结果输出:

0
1
1
2
3
5
8
13
21
34

yield 语句的特性是会在暂停时保存整个生成器的状态,包括所有的局部变量的值。在下一次调用生成器时,它会从上一次 yield 语句的位置开始执行,并且所有的局部变量都会恢复到上一次暂停时的状态。

具体到这个斐波那契数列的例子,当执行到 yield a 时,生成器会返回当前的 a 值,然后暂停。在暂停时,ab 的值会被保存,下一次调用生成器时,它会从上一次暂停的地方继续执行,而此时 ab 的值是上一次暂停时的值。

所以,yield 语句不仅返回一个值,而且它会保存整个生成器的状态,包括所有的局部变量。这就是为什么在下一次调用时,ab 的值会正确更新的原因。这种机制使得生成器可以在迭代过程中保持状态,而不是一次性生成所有的值。