python面向对象设计

面向对象程序设计

类与对象

1、类与对象的基本概念:
  • 在Python中,类是一种数据结构,用于封装数据以及操作数据的方法。
  • 对象是类的实例,它可以访问类中定义的属性和方法。

定义一个类:

在python中通常用class来定义一个类,命名的话通常采用驼峰命名法,即每个单词的首字母大写。

代码示例:

创建一个类

class MyClass:
    pass

定义一个实例对象

ac = MyClass() 
2、属性与方法

往类中添加属性,类可以包含属性,即对象的状态。可以通过在类中直接赋值来定义属性。

class Person:
    # 定义属性
    name = "John"
    age = 30

方法是与类相关联的函数,它们定义了类的行为。在类的定义方法时,第一个参数通常是self,表示对当前对象的引用。

class Person:
    name = "John"
    age = 30

    # 定义方法
    def greet(self):
        return f"Hello, my name is {self.name} and I'm {self.age} years old."

访问属性和调用方法:

通过对象实例可以访问类的属性和调用类的方法。使用点号 . 来访问对象的属性和调用方法。

print(person.name)  # 输出:John
print(person.age)   # 输出:30
print(person.greet())  # 输出:Hello, my name is John and I'm 30 years old.
3、构造函数

构造函数在 Python 中是一个特殊的方法,用于在创建类的实例时进行初始化操作。在 Python 中,构造函数的名称是 __init__,它允许你在创建类的实例时执行必要的初始化操作,例如设置对象的属性或执行其他初始化任务。在构造函数中,你可以将传递给类的参数用于初始化对象的属性。

代码示例:

class Person:
    def __init__(self, name, age):
        # 初始化属性
        self.name = name
        self.age = age
    
    def greet(self):
        return f"Hello, my name is {self.name} and I'm {self.age} years old."

# 创建对象实例并传递参数
person1 = Person("Alice", 25)
person2 = Person("Bob", 30)

# 调用对象方法
print(person1.greet())  # 输出:Hello, my name is Alice and I'm 25 years old.
print(person2.greet())  # 输出:Hello, my name is Bob and I'm 30 years old.
4、析构函数

析构函数在 Python 中是一个特殊的方法,用于在对象被销毁时执行清理操作。在 Python 中,析构函数的名称是 __del__,它在对象被销毁时自动调用。需要注意的是,析构函数不是必须的,因为 Python 具有自动内存管理机制(垃圾回收器),它负责回收不再使用的对象。

析构函数基本语法:

class ClassName:
    def __del__(self):
        # 清理操作
        # 释放资源、关闭文件等

析构函数__del__在对象被销毁时会自动调用,它允许你执行必要的清理操作,例如释放资源、关闭文件、断开数据库连接等。然而,由于 Python 具有自动内存管理,所以通常情况下你不需要显式地定义析构函数

在实际编程中,通常推荐使用上下文管理器(with语句)来确保资源的正确释放。上下文管理器可以更加可靠地管理资源,确保在退出上下文时执行必要的清理操作,而不依赖于对象的销毁时机。

代码示例(采用上下文管理器来释放资源):

class FileHandler:
    def __enter__(self):
        self.file = open('example.txt', 'w')
        return self.file

    def __exit__(self, exc_type, exc_value, traceback):
        self.file.close()

# 使用上下文管理器
with FileHandler() as f:
    f.write('Hello, world!')

在上面的示例中,FileHandler 类实现了上下文管理器的协议,它在 __enter__ 方法中打开文件,并在 __exit__ 方法中关闭文件。通过使用 with 语句,可以确保文件在退出上下文时被正确关闭,即使发生异常也能够正常关闭文件,避免资源泄露。

类的封装

  1. 公有成员(Public):
    • 在 Python 中,所有没有以下划线 _ 开头的属性和方法都是公有的。
    • 公有成员可以在类的内部和外部访问。
  2. 私有成员(Private):
    • 以两个下划线 __ 开头的属性和方法是私有的。
    • 私有成员只能在类的内部访问,外部无法直接访问。
  3. 受保护成员(Protected):
    • 以一个下划线 _ 开头的属性和方法是受保护的。
    • 受保护成员只能在类的内部及其子类中访问,外部不建议直接访问

代码示例:

class MyClass:
    def __init__(self):
        self.public_attribute = 'Public'     # 公有属性
        self._protected_attribute = 'Protected'   # 受保护属性
        self.__private_attribute = 'Private'   # 私有属性

    def public_method(self):
        return 'This is a public method.'   # 公有方法

    def _protected_method(self):
        return 'This is a protected method.'   # 受保护方法

    def __private_method(self):
        return 'This is a private method.'   # 私有方法
    def set(self,value):
        self.__rivate_attribute = value		#私有成员可以在类的内部进行访问,外部无法直接访问
    def	get(self):
        return self.__rivate_attribute

# 创建对象
obj = MyClass()

# 访问公有成员
print(obj.public_attribute)   # 输出: Public
print(obj.public_method())    # 输出: This is a public method.

# 访问受保护成员
print(obj._protected_attribute)   # 输出: Protected
print(obj._protected_method())    # 输出: This is a protected method.

# 尝试访问私有成员,将会引发 AttributeError 错误
# print(obj.__private_attribute)
# print(obj.__private_method())

#利用私有成员的特性创建方法进行访问
value = 100
obj.set(value)
revalue = obj.get()
print(revalue)#输出100

与面向对象有关的python内置函数

1、super()函数

在 Python 中,当子类重写父类的方法时,可以使用 super() 来调用父类的方法,以便在子类中扩展父类方法的功能。

super函数的作用:

  1. 调用父类的方法:最常见的用法是在子类中重写父类的方法时,使用 super() 调用父类的方法,以便在子类中保留父类方法的功能,并在其基础上进行扩展。
  2. 支持多继承:当子类同时继承多个父类时,super() 会按照方法解析顺序(MRO,Method Resolution Order)依次调用各个父类的方法。

代码示例:

class ParentClass:
    def parent_method(self):
        print("Parent method")

class ChildClass(ParentClass):
    def child_method(self):
        super().parent_method()
        print("Child method")

# 创建子类对象并调用方法
child = ChildClass()
child.child_method()
# 输出:
# Parent method
# Child method

2、hasattr()函数、getsattr()函数和setattr()函数

hasattr() 是 Python 中的一个内置函数,用于检查对象是否具有指定的属性或方法。

函数语法:

hasattr(object, attribute)

参数介绍:

  • object 是要检查的对象。
  • attribute 是要检查的属性或方法的名称。

代码示例:

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

person = Person("Alice", 30)

print(hasattr(person, "name"))  # True,因为 person 对象有名为 "name" 的属性
print(hasattr(person, "age"))   # True,因为 person 对象有名为 "age" 的属性
print(hasattr(person, "say_hello"))  # False,因为 person 对象没有名为 "say_hello" 的方法

getattr() 是 Python 中的一个内置函数,用于获取对象的属性值。

函数语法:

getattr(object, name[, default])

参数介绍:

  • object:表示要获取属性的对象。
  • name:表示要获取的属性的名称。
  • default:可选参数,表示如果属性不存在时返回的默认值,默认为 None

代码示例:

class ParentClass:
    def parent_method(self):
        print("Parent method")

class ChildClass(ParentClass):
    def child_method(self):
        getattr(super(), 'parent_method')()  # 使用getattr()调用父类方法
        print("Child method")

# 创建子类对象并调用方法
child = ChildClass()
child.child_method()
# 输出:
# Parent method
# Child method

在上述代码中,getattr(super(), 'parent_method')() 这一行代码通过 getattr() 函数动态获取了父类 ParentClass 中的 parent_method 方法,并调用了该方法。这种方式在一些特定的场景下会比直接使用 super() 更加灵活,特别是当需要根据条件来选择调用不同的方法时。

setattr()函数

函数功能用于设置对象的属性值。

代码示例:

class MyClass:
    pass

obj = MyClass()
setattr(obj, 'attr', 42)
print(getattr(obj, 'attr'))  # 输出: 42

3、property()函数

函数原型:

property(fget=None, fset=None, fdel=None, doc=None)

参数介绍:

  • fget:用于获取属性值的方法(getter)。如果未提供,则属性将变为只读。
  • fset:用于设置属性值的方法(setter)。如果未提供,则属性将变为只读。
  • fdel:用于删除属性值的方法(deleter)。
  • doc:可选,属性的文档字符串。

该函数常用于定义类的属性,从而实现对属性的封装和控制。

class MyClass:
    def __init__(self):
        self._attr = None

    def get_attr(self):
        return self._attr

    def set_attr(self, value):
        self._attr = value

    def del_attr(self):
        del self._attr

    attr = property(get_attr, set_attr, del_attr)

obj = MyClass()
obj.attr = 42
print(obj.attr)  # 输出: 42

上面这些函数都可以进行重写的。

python对象的特殊方法

  1. __init__() 方法:用于在创建对象时执行初始化操作。

    class Animal:
        def __init__(self):
            self.name = ''
    
  2. __str__() 方法:用于获取对象的字符串表示。

    class Animal:
        def __str__(self):
            return 'Animal: ' + self.name
    
  3. __eq__() 方法:用于比较对象的相等性。

    class Animal:
        def __eq__(self, other):
            return self.name == other.name
    
  4. __copy__() 方法:用于创建对象的浅拷贝。

    class Animal:
        def __copy__(self):
            return Animal(self.name)
    
  5. __deepcopy__() 方法:用于创建对象的深拷贝。

    import copy
    
    class Animal:
        def __deepcopy__(self, memo):
            return Animal(copy.deepcopy(self.name))
    
  6. __del__() 方法:用于在对象被删除时执行一些操作。

    class Animal:
        def __del__(self):
            print('Animal: ', self.name, 'is deleted.')
    

类的继承

在面向对象编程中,继承是一种重要的概念,它允许一个类(称为子类或派生类)继承另一个类(称为父类、基类或超类)的属性和方法。子类可以重用父类的代码,并且可以添加自己的特定功能。python支持单继承和多继承两种方式。

1、单继承

单继承指一个子类只能继承一个父类的特性。

代码示例:

#代码原理架构
class ParentClass:
    # 父类的属性和方法

class ChildClass(ParentClass):
    # 子类继承父类,并可以添加自己的属性和方法
#代码实例
class Animal:
    def __init__(self, name):
        self.name = name

    def sound(self):
        pass

class Dog(Animal):
    def sound(self):
        return "Woof!"

# 创建子类对象
my_dog = Dog("Buddy")
print(my_dog.name)  # 输出: Buddy
print(my_dog.sound())  # 输出: Woof!

2、多继承

多继承指一个子类可以继承多个父类的特性。

代码示例:

#代码原理架构
class ParentClass1:
    # 父类1的属性和方法

class ParentClass2:
    # 父类2的属性和方法

class ChildClass(ParentClass1, ParentClass2):
    # 子类继承多个父类,并可以添加自己的属性和方法


#代码实例
class Bird:
    def fly(self):
        return "Flying"

class Horse:
    def run(self):
        return "Running"

class Pegasus(Bird, Horse):
    pass

# 创建子类对象
my_pegasus = Pegasus()
print(my_pegasus.fly())  # 输出: Flying
print(my_pegasus.run())  # 输出: Running

需要注意的是,在多继承中可能会出现方法解析顺序(MRO)的问题,Python 使用 C3 线性化算法来确定方法的搜索顺序。

继承是面向对象编程中非常强大的工具,它允许我们构建更加灵活和可复用的代码结构。但是设计的时候要保持清晰逻辑的设计思路,避免出现深度继承链和过度复杂的继承关系,保证代码的可读性。

Mixin类介绍

Mixin类是面向对象编程中的一个概念,用于在多重继承中重用类的方法和属性,以提高代码的复用性。Mixin类通常不会被单独实例化,而是作为其他类的父类,通过多重继承的方式将其方法和属性混入到目标类中。

注意事项:

  1. Mixin 实现的功能需要是通用的,并且是单一的,可按需继承。
  2. Mixin 只用于拓展子类的功能,不能影响子类的主要功能,子类也不能依赖 Mixin。比如上例中 Person 继承不同的 Mixin 只是增加了一些功能,并不影响自身的主要功能。如果是依赖关系,则是真正的基类,不应该用 Mixin 命名。
  3. Mixin 类自身不能进行实例化,仅用于被子类继承。

类的多态

多态允许不同类的对象对同一消息做出不同的响应。多态性允许以统一的方式处理不同类的对象,从而增加了代码的灵活性、可扩展性和可维护性。在面向对象编程中,多态性通常通过继承和方法重写来实现。

方法重写

方法重写(又称为覆盖)是子类修改从父类继承来的方法的过程。当子类需要一个与父类相同名称但实现逻辑不同的方法时,子类可以重写这个方法,提供新的实现逻辑。

重写语法规则:

在Python中,子类可以通过定义一个与父类方法同名的方法来重写父类的方法。当调用该方法时,Python会首先在子类中查找是否存在该方法的定义,如果存在,则调用子类中的方法;如果不存在,则会继续在父类及其继承链中查找。

代码示例:

class ParentClass:
    def method(self):
        print("Parent method")

class ChildClass(ParentClass):
    def method(self):
        print("Child method")

# 创建子类对象并调用方法
child = ChildClass()
child.method()  # 输出: Child method

在子类中,可以使用super()函数来调用父类的方法,从而在子类中扩展父类方法的功能。

class ParentClass:
    def method(self):
        print("Parent method")

class ChildClass(ParentClass):
    def method(self):
        super().method()  # 调用父类方法
        print("Additional functionality")

# 创建子类对象并调用方法
child = ChildClass()
child.method()
# 输出:
# Parent method
# Additional functionality

注意事项:

  • 命名一致性:子类重写方法时,方法名必须与父类中要重写的方法名完全一致,否则Python不会认为它是重写而是新增了一个方法。
  • 参数一致性:子类重写方法时,参数列表必须与父类中要重写的方法参数列表一致。
  • 继承深度:Python支持多重继承,因此在使用方法重写时,需要考虑继承链的深度,以避免混淆和歧义。
str和repr重写

这里的话我再讲一下对__str____repr__的重写。

class Person:
    __slots__ = ('name', 'age', 'sex')

    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex
def main():
    person_a = Person('Tom', 30, 'male')
    print(person_a)

if __name__ == "__main__":
    main()
|--------------------------------------|
#输出
<__main__.Person object at 0x0000021F485A4AC0>

|--------------------------------------|
class Person:
    __slots__ = ('name', 'age', 'sex')

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

    def __str__(self):
        return f"Name: {self.name}, Age: {self.age}, Sex: {self.sex}"

def main():
    person_a = Person('Tom', 30, 'male')
    print(person_a)  # 输出对象的字符串表示形式

if __name__ == "__main__":
    main()
    
#输出
Name: Tom, Age: 30, Sex: male

上面的第二个代码我通过重写__str__ 方法,实现了自定义对象的字符串的表示形式,使其更加直观和易读。

str()返回的用户看到的字符串,而repr()返回的程序开发人员看到的字符串,也就是说,repr()是为调试服务的。

class Person:
    __slots__ = ('name', 'age', 'sex')

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

    def __str__(self):
        return f"Name: {self.name}, Age: {self.age}, Sex: {self.sex}"
    __repr__ = __str__

def main():
    person_a = Person('Tom', 30, 'male')
    print(person_a)
    person_a

if __name__ == "__main__":
    main()
#输出
Name: Tom, Age: 30, Sex: male
getattr重写
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

person = Person("Alice", 30)
print(person.name)
print(person.sex)

-------------------
#输出结果
Alice
AttributeError: 'Person' object has no attribute 'sex'

这里的话报错的时候实际上是调用了getattr函数,所以下面我们队其进行重写,使其输出结果更加具有观赏性。

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def __getattr__(self.attr):
        if attr in ('name','age'):
            print(attr)
        else:
            print('no this attribute : %s'% attr)

person = Person("Alice", 30)
print(person.name)
print(person.sex)

--------------------
#输出结果
Alice
no this attribute : sex

特殊的变量:slots(插槽)

在python的实例化对象中,类属性和实例化属性都是可以动态增加的但动态的增加对程序的严谨性造成了困扰。由于一个类可以产生很多的实例化对象,如果每一个对象都对自己的属性进行定义,那么程序的可维护性极低,为了解决这个问题,在python中,提供了一个特殊的系统变量slots,它可以对我们实例化对象的属性名进行限制。

在 Python 中,__slots__ 是一个特殊的变量,用于限制类实例可以拥有的属性。使用 __slots__ 可以明确指定一个类中允许的属性,从而在一定程度上减少实例所占用的内存空间,提高程序的执行效率。

__slots__用于限制类实例可以拥有的属性,只有在__slots__中列出的属性名才会被允许,其他属性名则会引发AttributeError 异常。(注意:是类实例)

class Person:
    __slots__ = ('name', 'age')

    def __init__(self, name, age):
        self.name = name
        self.age = age
def main():
    person_a = Person('Tom',30)
    print(person_a.name)
if __name__ == "__main__":
    main()
#输出
Tom

上面的就是对slots的一种运用。再举几个例子,让各位对slots有一个更清晰的理解。

class Person:
    __slots__ = ('name', 'age')

    def __init__(self, name, age):
        self.name = name
        self.age = age
def main():
    person_a = Person('Tom',30)
    person_a.address = 'beijing' #不在slots列表中,而且person_a是实例化对象。
    Person.sex = 'male'
    print(person_a.name)
    print(person_a.adderess)
if __name__ == "__main__":
    main()
#输出
Tom
AttributeError: 'Person' object has no attribute 'address'

由于我已经设置了插槽,而且里面没有address属性,所以会爆出AttributeError错误,但是上面代码中的Person.sex = 'male’为什么没有报错,因为我是直接往类中添加属性,是不会报错的。

class Person:
    __slots__ = ('name', 'age')

    def __init__(self, name, age):
        self.name = name
        self.age = age
def main():
    person_a = Person('Tom',30)
    Person.sex = 'male'
    print(person_a.name)
    person_b = Person('Jack',25)
    print(person_b.sex)
if __name__ == "__main__":
    main()
#输出
Tom
male

我的代码中并未尝试为 person_b 实例对象添加 sex 属性,因此不会报错。在 main() 函数中,我只是创建了一个 person_b 实例对象,并尝试打印 sex 属性的值,但在 Person 类中并没有定义 sex 属性,因此打印的是类属性 sex 的值。

通过 Person.sex = 'male' 这行代码,我给 Person 类添加了一个类属性 sex,而不是给实例对象添加属性。因此,当我尝试访问 person_b.sex 时,由于 person_b 实例对象并没有 sex 属性,Python 解释器会尝试在类中查找这个属性,找到了类属性 sex,所以不会报错。

讲到这里差不多可以理清slots的作用了,实在理解不了的多看几遍就行了。

重载运算符

在 Python 中,可以通过定义特定的方法来实现运算符重载,从而改变内置类型的行为。以下是一些常见的运算符重载方法:

  1. __add__(self, other):实现加法运算符 + 的重载。
  2. __sub__(self, other):实现减法运算符 - 的重载。
  3. __mul__(self, other):实现乘法运算符 * 的重载。
  4. __truediv__(self, other):实现除法运算符 / 的重载。
  5. __floordiv__(self, other):实现地板除运算符 // 的重载。
  6. __mod__(self, other):实现取模运算符 % 的重载。
  7. __pow__(self, other[, modulo]):实现幂运算符 ** 的重载。
  8. __lt__(self, other):实现小于比较运算符 < 的重载。
  9. __le__(self, other):实现小于等于比较运算符 <= 的重载。
  10. __eq__(self, other):实现等于比较运算符 == 的重载。
  11. __ne__(self, other):实现不等于比较运算符 != 的重载。
  12. __gt__(self, other):实现大于比较运算符 > 的重载。
  13. __ge__(self, other):实现大于等于比较运算符 >= 的重载。
  14. __neg__(self):实现一元负号运算符 - 的重载。
  15. __pos__(self):实现一元正号运算符 + 的重载。
  16. __abs__(self):实现绝对值函数 abs() 的重载。
  17. __str__(self):实现 str() 函数的重载,用于将对象转换为字符串。
  18. __repr__(self):实现 repr() 函数的重载,用于返回对象的“官方”字符串表示形式。

这些方法中的每一个都接受两个参数:selfotherself 是方法所属的对象,other 是与之进行运算的另一个对象。

下面我举一个例子展示如何在python中重载加法运算符+:

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __add__(self, other):
        if isinstance(other, Point):
            return Point(self.x + other.x, self.y + other.y)
        else:
            raise TypeError("Unsupported operand type(s) for +: '{}' and '{}'".format(type(self), type(other)))

# 创建两个 Point 对象
point1 = Point(1, 2)
point2 = Point(3, 4)

# 使用加法运算符进行运算
result = point1 + point2
print(result.x,result.y)
-------------------------------
#输出结果
4 6

在这个例子中,我们定义了一个 Point 类,其中重载了 __add__() 方法来实现加法运算符的重载。当我们使用 + 运算符将两个 Point 对象相加时,__add__() 方法会被调用,并返回一个新的 Point 对象,其 xy 分别为两个对象的对应属性之和。

  • 21
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

密码小丑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值