【Python面向对象编程】

Python 是一种支持面向对象编程(Object-Oriented Programming, OOP)的语言,可以很容易在Python中创建一个类和对象。
面向对象编程是一种编程范式,它使用“对象”来设计应用和软件。在面向对象的程序中,数据(属性)和功能(方法)被封装在对象中,并通过对象间的交互来实现程序的功能。

一、面向对象的基本特征

  • 类(Class): 用来描述具有相同的属性和方法的对象的集合类是对象的蓝图或模板,它定义了对象的属性和方法
  • 对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
    对象是类的实例,具有类定义的属性和方法。
  • 属性(Attribute):属性是对象的数据部分,它存储了对象的状态信息。
  • 方法(Method):方法是对象的行为的定义,即对象可以执行的操作。即类中定义的函数。
  • 类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。
    类变量通常不作为实例变量使用。
  • 数据成员:类变量或者实例变量,用于处理类及其实例对象的相关的数据。
  • 方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,
    这个过程叫方法的覆盖(override),也称为方法的重写。
  • 局部变量:定义在方法中的变量,只作用于当前实例的类。
  • 实例变量:在类的声明中,属性是用变量来表示的。
    这种变量就称为实例变量,是在类声明的内部但是在类的其他成员方法之外声明的。
  • 继承:即一个派生类(derived class)继承基类(base class)的字段和方法。
    继承也允许把一个派生类的对象作为一个基类对象对待。
  • 实例化:创建一个类的实例,类的具体对象。

二、面向对象编程

1、类的使用

注意:需要在单独的文件中定义一个类。
类的一般语法:

class ClassName:
   '类的帮助信息'   #类文档字符串
   class_suite  #类体(由类成员、方法、数据属性组成)

示例1:

# 定义一个名为Dog的类 
class Dog:  
    '基类'
    
    dogCount = 0 #类变量

    def __init__(self, name, age):  
        # 初始化方法,当创建类的新实例时自动调用
        self.name = name  # 对象的属性
        self.age = age

        Dog.dogCount += 1
    def bark(self):
        # 类的方法
        print(f"{self.name} says Woof!")

    def totaldogCount(self):  
        # 类的方法
        print("Total Employee %d" % Dog.dogCount)
        

 # 创建类的实例(对象)通过 __init__ 方法接收参数
my_dog = Dog("Buddy", 3)

# 访问对象的属性,使用.来访问属性
print(my_dog.name)  # 输出:Buddy
print(my_dog.age)  # 输出:3

# 调用对象的方法
my_dog.bark()  # 输出:Buddy says Woof!
my_dog.totaldogCount()

运行结果:

Buddy
3
Buddy says Woof!
Total Employee 1

在这个示例中:定义了一个名为 Dog 的类,它有一个初始化方法 init 和两个方法 bark和totaldogCount。创建了一个 Dog 类的实例 my_dog设置了它的属性 name 和 age访问了 my_dog 的属性并调用了它的方法

  • dogCount 是类变量其值将在这个类的所有实例之间共享。可以在内部类或外部类使用 Dog.dogCount访问。
  • 第一种方法__init__()方法是一种特殊的方法,被称为类的构造函数或初始化方法,当创建了这个类的实例时就会调用该方法。
  • self 代表类的实例self 在定义类的方法时是必须有的,虽然在调用时不必传入相应的参数。

2、内置类属性

当创建一个类后,系统会自动为该类赋予一些内置类属性。这些属性名通常使用双下划线(__)包围,以与普通属性名区分。以下是一些常见的内置类属性:

  • init:类的初始化方法,当创建类的新实例时会自动调用。通常,我们在这里进行属性的初始化操作。__init__的第一个参数总是self,代表创建的实例本身。

  • new:一个静态方法,用于创建对象实例。它相当于构造器,负责对象的创建。__new__的第一个参数是cls,代表类本身。

  • str:一个特殊方法,用于定义当使用print函数或str()函数时对象的字符串表示形式。

  • doc:一个属性,用于存储对象的文档字符串。通过访问对象的__doc__属性,我们可以获取其文档字符串。

  • dict:这个属性可以作用在文件、类或类的对象上,最终返回的结果为一个字典,包含了对象或类的属性。

  • bases : 类的所有父类构成元素(包含了一个由所有父类组成的元组)

  • 还有其他一些内置类属性,如__file__、namemodulebase、__bases__等,它们各自具有特定的用途和含义。

这些内置类属性都是Python语言本身提供的,用于支持面向对象编程的各种特性和功能。
在编写Python代码时,了解这些内置类属性的用法和含义,可以帮助我们更有效地利用Python的面向对象编程特性。

对示例1查看内置类属性:

print ("Dog.__doc__:", Dog.__doc__)
print ("Dog.__name__:", Dog.__name__)
print( "Dog.__module__:", Dog.__module__)
print ("Dog.__bases__:", Dog.__bases__)
print ("Dog.__dict__:", Dog.__dict__)

运行结果:

Dog.__doc__: None
Dog.__name__: Dog
Dog.__module__: __main__
Dog.__bases__: (<class 'object'>,)
Dog.__dict__: {'__module__': '__main__', 'dogCount': 1, '__init__': <function Dog.__init__ at 0x00000281DAB9B380>, 'bark': <function Dog.bark at 0x00000281DAB9B420>, 'totaldogCount': <function Dog.totaldogCount at 0x00000281DAB9B4C0>, 'prt': <function Dog.prt at 0x00000281DAB9B560>, '__dict__': <attribute '__dict__' of 'Dog' objects>, '__weakref__': <attribute '__weakref__' of 'Dog' objects>, '__doc__': None}

3、self代表类的实例,而非类

示例2;

class onetest:
    def prt(self):
        print(self)
        print(self.__class__)


t = onetest()
t.prt()

class twotest:
    def prt(too):
        print(too)
        print(too.__class__)

运行结果:

<__main__.onetest object at 0x000002A8CA8CF1D0>
<class '__main__.onetest'>
<__main__.twotest object at 0x000002A8CA8CF200>
<class '__main__.twotest'>

这个示例可知,self代表的是类的实例,代表当前对象的地址,而self.__class__则是指向类。self不是关键字,换成其他的too也可以执行。

4、对象销毁(垃圾回收)

Python 使用了引用计数来跟踪和回收垃圾。

在 Python 内部记录着所有使用中的对象各有多少引用。
一个内部跟踪变量,称为一个引用计数器。

当对象被创建时, 就创建了一个引用计数, 当这个对象不再需要时, 也就是说, 这个对象的引用计数变为0 时, 它被垃圾回收。但是回收不是"立即"的, 由解释器在适当的时机,将垃圾对象占用的内存空间回收。

Python 的垃圾收集器是一个引用计数器和一个循环垃圾收集器。
__del__在对象销毁的时候被调用,当对象不再被使用时,__del__方法运行。
示例如下:

class demo:
    def __init__(self,x=0,y=0,z=0):
        self.x = x
        self.y = y
        self.z = z


    def add(self):
        self.z = self.x + self.y
        print(self.z)
        return self.z


    def __del__(self):
       class_name = self.__class__.__name__
       print(class_name, "销毁")


# 创建类的对象
dem = demo(1,2,5)

#访问对象的属性
print(dem.x,dem.y,dem.z)
#调用对象的函数or方法
dem.add()

# 再次创建类的对象
dem1 = demo()
dem2=dem1
dem3=dem2
print(id(dem1),id(dem2),id(dem3))

运行结果:

1 2 5
3
1602445688368 1602445688368 1602445688368
demo 销毁
demo 销毁

5、类的继承

类的继承允许创建一个新类(称为子类或派生类),继承自一个或多个已存在的类(称为父类或基类)。通过继承,子类可以自动获得父类的属性和方法,并可以添加或覆盖自己的属性和方法。这提供了一种重用代码和组织代码层次结构的强大机制

示例:

# 定义一个父类
class Animal:
    def __init__(self, name):
        self.name = name


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


# 定义一个子类,继承自Animal类
class Dog(Animal):
    def __init__(self, name, breed):
        # 调用父类的初始化方法
        super().__init__(name)

        self.breed = breed

    def speak(self):
        # 实现父类中未实现的方法
        return f"{self.name} barks!"


# 创建一个Dog类的实例
my_dog = Dog("小黄", "Labrador")

# 访问实例的属性
print(my_dog.name)  # 输出: 小黄
print(my_dog.breed)  # 输出: Labrador

# 调用实例的方法
print(my_dog.speak())  # 输出: 小黄 barks!

运行结果:

小黄
Labrador
小黄 barks!

在示例中,Dog 类继承了 Animal 类。
Dog 类通过调用 super().__init__(name) 来调用父类 Animal 的 init 方法,以初始化从 Animal 继承的属性 name。且Dog 类添加了自己的属性 breed,并实现了 speak 方法。

super() 函数是调用父类方法的一种常见方式。super() 返回一个临时对象,它绑定到父类,并允许调用父类的方法。这种方式特别有用当类继承自多个父类,且需要明确调用特定的父类方法时。

类的继承可以形成复杂的层次结构,一个类可以继承自多个父类(多重继承),但通常应该谨慎使用,以避免复杂的继承和复杂的依赖关系。在Python中,多重继承是允许的,但可能会导致一些难以预料的行为,特别是在处理方法解析顺序(MRO,Method Resolution Order)时。

注意点:
(1) 若子类需要覆盖父类的方法(即使用自己的实现替代父类的实现),可以简单地在子类中定义同名方法。当在子类的实例上调用该方法时,Python 会优先使用子类中的定义
示例:


class Parent:  # 定义父类
    def myMethod(self):
        print('调用父类方法')


class Child(Parent):  # 定义子类
    def myMethod(self):
        print('调用子类方法')

# 创建子类实例
dem = Child()
dem.myMethod()  # 子类调用重写方法

运行结果:

调用子类方法

(2) 若父类中的方法不应该被子类直接覆盖,或者需要在子类实现之前执行一些操作,可以在父类中使用 raise NotImplementedError 来抛出一个异常。这样,若子类没有实现该方法,当尝试调用时就会触发异常。

6、类的私有(属性和方法)

(1)类的私有属性

通过在属性名前面加上两个下划线(__)来定义的。这是一种约定俗成的命名方式,不是Python本身的一种强制机制。Python会将这样的属性名“变形”(mangle),使其变成在类外部无法直接访问的形式。但这不意味着私有属性是完全不可访问的,它只是一种防止在类外部直接访问的机制。

示例:

class MyClass:
    def __init__(self):
        self.__private_attribute = "This is a private attribute"

    def get_private_attribute(self):
        return self.__private_attribute

# 尝试直接访问私有属性(会报错)
# instance = MyClass()
# print(instance.__private_attribute)  # AttributeError: 'MyClass' object has no attribute '__private_attribute'

# 通过类内部提供的方法访问私有属性
instance = MyClass()
print(instance.get_private_attribute())  # 输出: This is a private attribute

以上示例中,__private_attribute是一个私有属性,它在类的外部是不可见的。尝试直接访问它会引发一个AttributeError。但是,通过在类内部定义一个方法(如get_private_attribute),则可对私有属性进行访问。

注意:
Python没有真正的私有属性。

  • 虽然变形机制使得私有属性在类外部难以直接访问,但仍然可以通过类的__dict__属性或者使用getattr函数,结合属性名的变形规则来访问私有属性。

  • 子类也可以访问父类的私有属性

  • 私有属性主要是一种编程约定,用于表明某些属性不应该在类的外部被直接访问或修改。

  • 私有属性通常用于存储那些不应该被外部直接修改的内部状态,或者用于实现类的内部逻辑。通过提供公共的方法来访问和修改这些属性,可以更好地控制对属性的访问,并隐藏实现细节。

(2)类的私有方法

通过在方法名前面加上两个下划线(__)来定义的。和私有属性一样,私有方法并不是完全不可访问的。它们仍然可以通过类的内部逻辑或继承关系被访问,只是不鼓励在类外部直接调用。
示例:

class MyClass:
    def __init__(self):
        self.public_attribute = "Public"

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

    def __private_method(self):
        print("This is a private method.")
        # 这里可以访问和修改类的私有属性和其他方法

    def call_private_method(self):
        # 通过内部方法调用私有方法
        self.__private_method()
        
        
 # 尝试直接调用私有方法(会报错)
# instance = MyClass()
# instance.__private_method()  # AttributeError: 'MyClass' object has no attribute '__private_method'

# 通过公共方法间接调用私有方法
instance = MyClass()
instance.call_private_method()  # 输出: This is a private method.

这个示例中,__private_method是一个私有方法。尝试在类外部直接调用它会引发一个AttributeError。但是,通过在类内部定义一个公共方法(如call_private_method),可间接地调用私有方法。

注意:
Python并没有真正的私有方法。同私有属性。

在实际编程中,私有方法通常用于实现类的内部逻辑,这些逻辑不需要被类的外部用户知道或调用。通过只提供公共的接口来访问和修改类的状态,可以更好地封装类的实现细节,并控制对类内部逻辑的访问。

7、关于重载

在Python中,类方法或函数的重载与一些其他编程语言(如Java或C++)中的概念有所不同。在Java或C++中,方法重载是指可以定义多个同名函数,但它们具有不同的参数类型或数量。然而,Python并不直接支持这种形式的重载

在Python中,函数或方法的参数类型和数量在定义时并不固定,也就是说,Python是一种动态类型语言。因此,Python的函数可以接受任意数量和类型的参数。这意味着Python的函数在某种程度上已经具有某种“重载”的能力,即同一个函数可以处理不同类型的参数或不同数量的参数。

虽Python没有直接的方法重载语法,但可通过使用默认参数和可变参数来实现类似的效果。例如,定义一个函数,它接受一个或两个参数,根据传递的参数数量执行不同的操作。当只传递一个参数时,它执行一种操作;当传递两个参数时,它执行另一种操作。

示例:

print("-----运算符重载----")
class Vector:
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def __str__(self):
        return 'Vector (%d, %d)' % (self.a, self.b)

    def __add__(self, other):
        return Vector(self.a + other.a, self.b + other.b)


v1 = Vector(5, 20)
v2 = Vector(10, -2)
print(v1 + v2)  #输出 Vector (15, 18)

此外,Python中的类方法也可以利用这种机制来模拟方法重载。通过定义具有默认参数的类方法,可以根据传递的参数的不同来执行不同的操作。

注意:

虽Python可通过这种方式模拟方法重载,但并不是Python的推荐做法。
Python的设计哲学更倾向于简洁和明确,通常更倾向于使用明确的函数或方法名来描述其功能,而不是依赖于参数的数量或类型来决定函数的行为。

8、“魔法”之特殊功能

在python中方法名如果是__xxxx__()的,那么就有特殊的功能。对此先介绍单下划线、双下划线、头尾双下划线。

单下划线(_):例如_foo 它表示这个变量或函数是类的内部使用,不建议在类外部直接访问。主要用作一个“弱内部使用”的标记。

双下划线(__):表示的是私有类型(private)的变量,这种私有性并不是绝对的,仍然可以通过一些方式访问和修改这些变量或函数。

头尾双下划线(如__var__):例如__init__用于初始化对象,__str__用于定义对象的字符串表示等。这些方法在Python的魔术方法(magic methods)中扮演着重要角色,用于实现对象的各种特殊操作。一般是系统定义名字 。

示例:

class  House:
    '定义一个雨类'

    def __init__(self,newname,newvalue):#初始化方法
        self.newname  = newname
        self.newvalue = newvalue

    def __str__(self):
        return "your house name is :%s,value is %d" %(self.newname,self.newvalue)

    def introduce(self):
        print("你的房子:%s, 价值:%d" % (self.newname, self.newvalue))

#创建一个对象
Lily = House("茅屋",10000000)
print("关于对象属性介绍",Lily.newname,Lily.newvalue) #输出:关于对象属性介绍 茅屋 10000000
print(Lily)  #输出: your house name is :茅屋,value is 10000000
Lily.introduce() #输出: 你的房子:茅屋, 价值:10000000
  • 当使用print输出对象的时候,只要定义了__str__(self)方法,那么就会打印从在这个方法中return的数据。
  • __str__方法需要返回一个字符串,当做这个对象的描写。

相关推荐

  1. Python面向对象编程

    2024-04-07 05:42:04       42 阅读
  2. Python面向对象编程

    2024-04-07 05:42:04       19 阅读
  3. python面向对象编程

    2024-04-07 05:42:04       8 阅读
  4. Python学习笔记(六)面向对象编程

    2024-04-07 05:42:04       39 阅读
  5. python学习】面向对象编程1

    2024-04-07 05:42:04       28 阅读
  6. python学习】面向对象编程3

    2024-04-07 05:42:04       31 阅读
  7. Python中的面向对象编程

    2024-04-07 05:42:04       38 阅读
  8. Python】复习7:面向对象编程(OOP)

    2024-04-07 05:42:04       18 阅读
  9. Python进阶(三)】——面向对象编程

    2024-04-07 05:42:04       20 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-07 05:42:04       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-07 05:42:04       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-07 05:42:04       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-07 05:42:04       18 阅读

热门阅读

  1. 深入理解Transformer架构的编码器-解码器结构

    2024-04-07 05:42:04       24 阅读
  2. 在使用clickhouse的一些心得

    2024-04-07 05:42:04       21 阅读
  3. RobotFramework测试框架(6)测试用例文件结构

    2024-04-07 05:42:04       29 阅读
  4. 面对对象编程(四)

    2024-04-07 05:42:04       17 阅读
  5. leetcode 169.多数元素

    2024-04-07 05:42:04       45 阅读
  6. ROC与决策树介绍

    2024-04-07 05:42:04       22 阅读
  7. 在 HTML 中禁用 Chrome 浏览器的 Google 翻译功能

    2024-04-07 05:42:04       36 阅读
  8. MongoDB的简单使用

    2024-04-07 05:42:04       20 阅读
  9. leetcode 72.编辑距离

    2024-04-07 05:42:04       22 阅读
  10. 深入了解go的通道类型

    2024-04-07 05:42:04       16 阅读
  11. 外刊杂志经济学人获取方式

    2024-04-07 05:42:04       18 阅读
  12. golang mutex

    2024-04-07 05:42:04       18 阅读
  13. 【Rust】基础语法

    2024-04-07 05:42:04       20 阅读