python 面向对象

1、面向对象

Python 是这一种完全支持面向对象编程(OOP)的语言。面向对象编程的核心思想就是将数据和处理数据的方法组织在一个对象中。Python中的面向对象编程特性包含类的定义、继承、封装、多态。

1.1 类和对象

类是对象的蓝图或模版,定义了对象的属性和行为。对象则是类的一个实例。

示例:

class Dog:
    species = 'Canis familiaris'  # 类变量

    def __init__(self, name, age):  # 初始化方法
        self.name = name # 实例变量
        self.age = age

    def bark(self):  # 实例方法
        print(f'{self.name} says Woof!')

dog1 = Dog('Fido', 5)  # 创建对象
dog1.bark()  # 调用方法

类变量

类变量是在类定义中定义的变量,而不是在任何方法内定义的。类变量对所有实例都是共享的,也就是说,所有实例都指向同一个内存位置。如果类变量被修改,那么所有的实例都会看到这个变化。

示例:

class MyClass:
    class_var = 'Shared among all instances'

instance1 = MyClass()
instance2 = MyClass()

print(instance1.class_var)  # 输出: Shared among all instances
print(instance2.class_var)  # 输出: Shared among all instances

MyClass.class_var = 'Modified'
print(instance1.class_var)  # 输出: Modified
print(instance2.class_var)  # 输出: Modified

实例变量

实例变量是在实例化对象时或者在实例方法中定义的变量。每个实例都有自己的副本,因此改变一个实例变量不会影响其他实例。

示例:

class MyClass:
    def __init__(self, instance_var):
        self.instance_var = instance_var

instance1 = MyClass('Unique to instance1')
instance2 = MyClass('Unique to instance2')

print(instance1.instance_var)  # 输出: Unique to instance1
print(instance2.instance_var)  # 输出: Unique to instance2

instance1.instance_var = 'Changed for instance1'
print(instance1.instance_var)  # 输出: Changed for instance1
print(instance2.instance_var)  # 输出: Unique to instance2

私有属性和私有方法

Python 中并没有真正的私有成员,但是有一些约定可以用来表示某个属性或方法不应该被外部直接访问。通常,这种约定是通过以下方式实现的:

  • 单下划线 _:表示该属性或者方法时受保护的,虽然可以被外部访问,但应被视为内部使用。
  • 双下划线 __:这是 Python用于名称改编的技术,它会在类的内部自动更改属性或方法的名字,使得外部直接访问变得困难。

示例

class MyClass:
    def __init__(self):
        self._protected_var = 0  # 受保护的变量
        self.__private_var = 0   # 私有变量

    def public_method(self):
        print("This is a public method.")

    def _protected_method(self):
        print("This is a protected method.")

    def __private_method(self):
        print("This is a private method.")

# 创建一个实例
instance = MyClass()

# 访问受保护的变量和方法
print(instance._protected_var)  # 输出: 0
instance._protected_method()    # 输出: This is a protected method.

# 尝试访问私有变量和方法
# 直接访问会失败
# print(instance.__private_var)  # 会抛出 AttributeError
# instance.__private_method()    # 会抛出 AttributeError

# 通过名称改编访问私有变量和方法
print(instance._MyClass__private_var)  # 输出: 0
instance._MyClass__private_method()    # 输出: This is a private method.

总结

  • 类变量:所有实例共享的变量。
  • 实例变量:每个实例独有的变量。
  • 私有属性/方法:通过约定(单下划线 _)或名称改编(双下划线 __)来表示不应该被外部直接访问的属性或方法。

1.2 封装

封装是指隐藏对象的内部状态和细节,并仅通过对象提供的公共接口来访问。Python中没有严格意义上的私有成员,但可以通过命名约定(如以单下划线_ 受保护:只允许其本身和子类进行访问或双下划线 __ 私有变量:只允许类本身进行访问开头)来暗示某个属性或方法不应该在类外部使用。

class BankAccount:
    def __init__(self):
        self._balance = 0  # 保护成员

    def deposit(self, amount):
        self._balance += amount

    def get_balance(self):
        return self._balance

account = BankAccount()
account.deposit(100)
print(account.get_balance())  # 100

1.3 继承

继承允许我们定义一个类(子类)从另一个类(父类或超类)继承属性和方法。这样有助于重用代码并建立类之间的层次关系

示例:

class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        raise NotImplementedError('Subclass must implement this abstract method')

class Dog(Animal):  # Dog 类继承自 Animal 类
    def speak(self):
        return self.name + ' says Woof!'

class Cat(Animal):
    def speak(self):
        return self.name + ' says Meow!'

fido = Dog('Fido')
isis = Cat('Isis')

print(fido.speak())  # Fido says Woof!
print(isis.speak())  # Isis says Meow!

super()

在 Python 中,super() 函数是一个内置函数,用于调用父类(超类)的一个方法。它在多继承 的情况下特别有用,因为它可以帮助正确地管理方法解析顺序(MRO,Method Resolution Order),确保方法按照预期的顺序被调用。

使用 super() 的基本语法

在 Python3 中,使用 super() 通常比在 Python2 中更简单。在Python3 中, super() 不需要显示地传递当前类和当前对象作为参数,因为它们会自动传递给

super()

示例:

class Animal:
    def __init__(self, name):
        self.name = name
        print(f"Animal {self.name} created")

    def speak(self):
        raise NotImplementedError('Subclass must implement this abstract method')

class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name)  # 调用父类的构造函数
        self.breed = breed
        print(f"Dog {self.name} of breed {self.breed} created")

    def speak(self):
        print(f"{self.name} says Woof!")

class Cat(Animal):
    def __init__(self, name, color):
        super().__init__(name)  # 调用父类的构造函数
        self.color = color
        print(f"Cat {self.name} of color {self.color} created")

    def speak(self):
        print(f"{self.name} says Meow!")

dog = Dog("Rex", "Labrador")
dog.speak()

cat = Cat("Tom", "White")
cat.speak()

在多继承中使用

当一个类继承自多个父类时,super() 会更加 MRO 来确定调用哪个父类方法。Python 使用 C3 线性化算法来决定 MRO

示例:

class A:
    def method(self):
        print("A method")

class B(A):
    def method(self):
        print("B method before A")
        super().method()
        print("B method after A")

class C(A):
    def method(self):
        print("C method before A")
        super().method()
        print("C method after A")

class D(B, C):
    def method(self):
        print("D method before B and C")
        super().method()
        print("D method after B and C")

d = D()
d.method()

在这个例子中,D 类继承自 BC,而 BC 都继承自 A。当我们调用 D 类的 method 方法时,super().method() 会根据 MRO 调用 B 类的 method 方法,然后继续调用 C 类的 method 方法,最后调用 A 类的 method 方法。

MRO 的 顺序

可以通过 mro() 方法查看类的方法解析顺序:

class A:
    pass

class B(A):
    pass

class C(A):
    pass

class D(B, C):
    pass

print(D.mro())  # 输出: [__main__.D, __main__.B, __main__.C, __main__.A, object]

总结

  • super() 用于调用父类的方法,帮助管理继承关系中的方法调用。
  • 在 Python 3 中,super() 的调用通常更加简洁,不需要显式传递参数。
  • 在多继承的情况下,super() 会根据 MRO 确定方法的调用顺序,确保方法按正确的顺序被调用。

1.4 多态

多态是指允许不同类的对象对同一消息做出响应的能力。即,不同对象会根据其特定的类来执行适当的行为

示例

def make_sound(animal_type):
    if isinstance(animal_type, Dog):
        return animal_type.speak()
    elif isinstance(animal_type, Cat):
        return animal_type.speak()

dog = Dog('Rex')
cat = Cat('Tom')

print(make_sound(dog))  # Rex says Woof!
print(make_sound(cat))  # Tom says Meow!

2、类装饰器使用

2.1 @classmethod 类方法

Python 类中,@classmethod 修饰符对应的函数不需要实例化,不需要 self, 但是第一个参数需要表示自身类的 cls,以用来调用类的属性、类的方法,实例化对象等。

2.2 @staticmethod 静态方法

静态方法没有类似 **selfcls**这样的特殊参数,因此Python解释器不会对它包含的参数做任何类或者对象的绑定。也正因为如此,类的静态方法中也无法调用任何属性和类方法。

静态方法的调用:既可以直接使用类名,也可以使用类对象

2.3@property属性方法

Python 的属性装饰器允许你将方法的行为像访问普通属性一样进行访问,而不是像函数那样调用。这使得你可以控制对类的属性访问和修改方式,
同时保持代码的简洁性和易读性。Python 提供了三个主要装饰器来帮助实现这一点:

  • @property:用于定义一个 getter 方法,使得该方法可以像访问属性一样被调用,而不需要加括号
  • @.setter:用于定义一个 setter 方法,允许你为属性赋值时执行某些操作
  • @.deleter:用于定义一个 deleter 方法,允许你在删除属性时执行某些操作
class Temperature:

    def __init__(self, celsius):
        self._celsius = celsius

    @property
    def celsius(self):
        """获取摄氏温度"""
        print("Getting value")
        return self._celsius

    @celsius.setter
    def celsius(self, value):
        """设置摄氏温度"""
        if value < -273.15:
            raise ValueError("Temperature below -273.15 is not possible.")
        print("Setting value")
        self._celsius = value

    @celsius.deleter
    def celsius(self):
        """删除摄氏温度属性"""
        del self._celsius

    @property
    def fahrenheit(self):
        """计算并返回华氏温度"""
        return self._celsius * 9 / 5 + 32

    @fahrenheit.setter
    def fahrenheit(self, value):
        """通过华氏温度设置摄氏温度"""
        self.celsius = (value - 32) * 5 / 9


if __name__ == '__main__':
    t = Temperature(10)

    # 使用 getter
    print(t.celsius)

    # 设置新值
    t.celsius = 20
    print(t.celsius)

    # 使用另一个属性,背后调用了 setter
    t.fahrenheit = 68  # 68 华氏度转化成摄氏度约为20度
    print(t.celsius)  # 输出 20

    del t.celsius  # 删除属性

3、抽象类(Abstract Class)

在Python 中,抽象基类(Abstract Base Classes, ABCs)通常用来实现类似于其他语言中的接口功能,但Python本身并不能直接支持接口这个概念。在Python3,抽象类主要通过abc模块来定义。

抽象类是一种不能被实例化的类,它通常包含一个或多个抽象方法。抽象方法是没有具体实现方法,需要由继承该抽象类的具体子类来实现。

在Python 中,可以通过导入 abc 模块并使用 ABCMeta 元类 和 abcstractmethod 装饰器来定义抽象类和抽象方法

示例:

from abc import ABCMeta, abstractmethod


class MyAbstractClass(metaclass=ABCMeta):
    @abstractmethod
    def do_something(self):
        pass


class MyClass(MyAbstractClass):
    def do_something(self):
        print("Doing something")


my_instance = MyClass()
my_instance.do_something()  # 输出 "Doing something"

4、接口(Interface)

在许多面向对象的语言中,接口是一个特殊的抽象类,它只包含方法签名而没有任何方法实现。所有实现该接口的类都必须提供这些方法的实现。

由于Python 是动态语言型语言,实际上任何类都可以作为接口使用,只要其他类提供了相同的方法签名。但是为了明确表明某个类应该被视为接口,可以使用抽象类的方式。

示例

from abc import ABCMeta, abstractmethod


class MyInterface(metaclass=ABCMeta):
    @abstractmethod
    def method_one(self):
        pass

    @abstractmethod
    def method_two(self):
        pass


class MyClassImplementingInterface(MyInterface):
    def method_one(self):
        print("Method one implemented")

    def method_two(self):
        print("Method two implemented")


my_instance = MyClassImplementingInterface()
my_instance.method_one()  # 输出 "Method one implemented"
my_instance.method_two()  # 输出 "Method two implemented"

总结

  • 抽象类:可以有具体的实现细节,也可以有抽象方法需要子类实现
  • 接口:通常指的是一组抽象方法,没有任何实现细节,只是定义了一个类需要遵循的契约

基于抽象类实现工厂设计模式的案例

from abc import ABCMeta, abstractmethod


class Employee(metaclass=ABCMeta):
    """员工(抽象类)"""

    def __init__(self, name):
        self.name = name

    @abstractmethod
    def get_salary(self):
        """结算月薪"""
        pass


class Manager(Employee):
    """部门经理"""

    def get_salary(self):
        return 15000


class Programmer(Employee):
    """程序员"""

    def __init__(self, name, working_hour=0):
        self.working_hour = working_hour
        super().__init__(name)

    def get_salary(self):
        return 200.0 * self.working_hour


class Salesman(Employee):
    """销售员"""

    def __init__(self, name, sales=0.0):
        self.sales = sales
        super().__init__(name)

    def get_salary(self):
        return 1800.0 + self.sales * 0.05


class EmployeeFactory():
    """创建员工的工厂(工厂模式 - 通过工厂实现对象使用者和对象之间的解耦合)"""

    @staticmethod
    def create(emp_type, *args, **kwargs):
        """创建员工"""
        emp_type = emp_type.upper()
        emp = None
        if emp_type == "M":
            emp = Manager(*args, **kwargs)
        elif emp_type == "P":
            emp = Programmer(*args, **kwargs)
        elif emp_type == "S":
            emp = Salesman(*args, **kwargs)
        return emp


if __name__ == '__main__':
    emps = [
        EmployeeFactory.create("M", "曹操"),
        EmployeeFactory.create("P", "荀彧", 120),
        EmployeeFactory.create("P", "郭嘉", 85),
        EmployeeFactory.create("S", "典韦", 120000)
    ]
    for emp in emps:
        print("%s: %.2f元" % (emp.name, emp.get_salary()))

5、反射

在 Python 中,**反射(Reflection)**是一种机制,它允许运行中的程序检查和 反射自身的结构和行为,并且可以动态地调用对象的方法和属性。Python 提供了几个内置函数来帮助实现反射,比如 dir()getattr()hasattr()setattr()、和 delattr() 等。

5.1 dir()

dir() 函数返回一个列表,包含了对象的所有属性和方法的名字

class MyClass:
    def __init__(self):
        self.a = "Hello"
    
    def greet(self):
        print("Hello, world!")

obj = MyClass()
print(dir(obj))  # 输出所有属性和方法名

5.2 getattr()

getattr() 函数用于获取对象的属性值。如果指定的属性不存在,则可以提供一个默认值作为第二个参数

print(getattr(obj, 'a'))  # 输出: Hello
print(getattr(obj, 'b', 'default'))  # 输出: default (因为没有属性'b')

5.3 hasattr()

hasattr() 函数用来判断对象是否包含指定的属性

print(hasattr(obj, 'a'))  # 输出: True
print(hasattr(obj, 'b'))  # 输出: False

5.4 setattr()

setattr() 函数用来设置对象的属性值。如果属性不存在,则会被创建;如果存在,则会被修改

setattr(obj, 'b', 'world')
print(obj.b)  # 输出: world

5.5 delattr()

delattr() 函数用来删除对象的指定属性

5.6 动态调用方法

除了操作属性外,反射还可以用来动态调用方法

method_name = 'greet'
if hasattr(obj, method_name) and callable(getattr(obj, method_name)):
    getattr(obj, method_name)()

6、callable() 的用法

在 Python 中,callable() 是一个内置函数,用于检查一个对象是否可被调用 (callable)。换句话说,它会检查该对象是否会像函数一样能够被调用。如果对象是可调用的,那么 callable() 将返回 True,否则返回 False

在Python中,一个 对象被认为是可调用的,如果它定义了 __call__ 方法。这个方法 允许类的实例像函数那样被调用。

示例代码

def my_function():
    return "Hello, world!"

# 检查函数是否可调用
print(callable(my_function))  # 应该输出 True

# 定义一个类
class Greeter:
    def __init__(self, message):
        self.message = message
    
    def __call__(self):
        return self.message

# 创建一个实例
greeter = Greeter("Hi there!")

# 检查实例是否可调用
print(callable(greeter))  # 应该输出 True

# 调用实例
print(greeter())  # 输出: Hi there!

# 检查字符串是否可调用
print(callable("I am not callable"))  # 应该输出 False

my_function 是一个普通的函数,所以它是可调用的。Greeter 类定义了一个 call 方法,因此它的实例 greeter 也是可调用的。而字符串 “I am not callable” 不是可调用的,因此 callable() 返回 False。

使用场景

callable() 经常用于需要动态决定是否调用某个对象的情况下,特别是在处理回调函数或者插件系统中。例如,当编写一个框架时,可能希望让用户能够通过定义特定的方法来扩展框架的功能。在这种情况下,可以使用 callable() 来检查用户是否已经定义了相应的可调用对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值