Python面向对象编程基础:类、实例与核心语法详解
本文深入解析Python面向对象编程核心概念,从“一切皆对象”出发,通过实例阐述类与实例的关系。详细讲解类的基本语法、新旧式类的区别(Python 2.2引入type统一),以及__init__、__new__和__del__等关键特殊方法的用法与注意事项,帮助开发者掌握Python类的高级特性。
在着手撰写Python面向对象编程的内容时,我曾感到十分纠结。由于Python中面向对象涉及的知识点繁多,若要彻底讲透,似乎需要以面向对象的思想重新梳理之前的所有内容。鉴于这个话题覆盖面广,经过反复权衡,本文仅选取重点内容进行讲解,其余部分需读者自行拓展学习。
在此声明,本文基于Python 2.7版本编写,不同版本间可能存在差异。
在正式讲解前,我们先通过一段代码思考一个问题:

为什么我只为变量a赋值,就能调用一些我并未定义的方法?
部分读者可能会回答:因为a此时是一个字符串对象,自然可以使用字符串内置的方法,这些方法是Python预先定义好的。
那么,另一个问题是:Python是如何判断它属于字符串对象的?
在深入探讨之前,请记住一个核心概念:在Python中,一切皆对象,而所有对象均由类创建。
那么,究竟什么是类?这里举一个生活中的例子。
假设我在街上行走,迎面遇到一个仇人。我想教训他,但考虑到力的相互作用,打他我的拳头也会疼。此时,我注意到路边有一块大小形状合适的石头。在不考虑法律后果的前提下,这是一个理想的选择。于是,我捡起石头对仇人实施了攻击(此处为比喻,请勿模仿),然后低调离开。
在这个过程中,我们是如何识别出那块物体是石头的?
因为它具备石头的外观特征。
我们又是如何知道石头可以用来砸人?
因为石头具有坚硬的特性。
我们又是如何知道石头是坚硬的?
这源于过往的经验,比如小时候长辈的教导。
由此得出结论:我们之所以知道某物是什么、具备何种功能,是因为脑海中已存在该物体的概念。这个概念可能来自长辈传授,也可能通过自身实验总结得出。这类似于科学家对“黑洞”等概念的研究与总结过程。
若将上述“用石头打人”的例子转化为代码,实现如下:
class Stone(object): # 我创建一个叫Stone的类
def attack(self):
print '把头伸过来,我给你加个buff'
a = Stone() # 我用类创建了一个对象,也称为类的实例化
a.attack() # 我使用这个对象的方法

通过这种方式,我们便实现了所需的功能。
如果我们尝试创建其他对象:

显然,Python并未为其他对象提供attack方法,因此无法调用。
类仅是一种抽象定义,而实例则是具体的对象。两者的区别如同我脑海中的石头概念与我手中实际的石头,只有后者真实存在。此处无需探讨唯心主义等哲学问题。
至此,类与对象之间的关系应已清晰。部分同学可能会问:Python中的类是否都是预先写好的?是否存在让Python自动生成类并在特定时刻使用的方法?如果这一设想实现,其效果将接近人类的学习能力,这也触及了人工智能的核心概念。虽然目前尚无法实现真正的人工智能,但编写智能程序可能是程序员的终身追求。
在理解类与实例化之后,我们来看类的基本语法。
首先,与定义函数使用的def关键字类似,class是定义类的关键字。
紧接着是类名,可自定义,但需避免与Python内置关键字冲突。建议同时避开Python的内建类型名称,如str、int等。规范的命名应遵循“驼峰命名法”,例如MyClass,即每个单词首字母大写。
随后是一个括号,其中的参数用于继承,通常继承自object,表示这是一个新式类。你可能见过没有括号的写法,那是经典类的写法。
示例如下:
class NewClass(object): pass class OldClass: pass New = NewClass() # 创建一个新式类的实例 Old = OldClass() # 创建一个经典类的实例
这就是类的基本语法。虽然这还不够完整,但在深入之前,我想先讲解新式类与经典类的区别。
在此,我们先打印两个变量的类型:
print type(New) print type(Old)

可以看到两者的输出结果不同。
在Python 2.2之前的版本中,仅存在经典类这一种写法,当时类与类型尚未合并。
类是类对象,实例是实例对象,两者之间没有任何关联。
这句话的具体含义可通过代码观察:
print type(OldClass) print type(Old)

我们可以发现输出结果较为模糊。在经典类中,无论属于哪个类,统一归类为“类”类型;实例则统一为“实例”类型,无法区分其由哪个类创建。也就是说,当时的类型用classobj和instance代表了所有类和实例,无论具体是哪个类或由其创建的实例。
由于此类信息过于匮乏,且类与类型之间关系混乱,Python 2.2引入了新式类,并实现了类与类型的统一。
print type(NewClass) print type(New)

类的类型是type?type返回的对象能否像类一样创建新对象?
总结来说:在新式类中,所有类对象都是type的实例。而不同的类对象能够创建出对应的实例。
class NewClass(object):
def __init__(self, val):
self.val = val
New = NewClass(123)
b = type(New)(321) # 对实例来说type返回的是类对象,我又可以用类对象来和创建新的实例
print b.val

构造器方法
通常理解,类中的函数即为方法。方法主要分为三类:实例方法,需实例化后调用,第一个参数通常为self,代表实例本身;类方法,第一个参数为cls,代表类本身;静态方法,即普通函数,对参数无特殊要求。
1. __init__(self [,arg1,....]):
当类被调用进行实例化时,Python会自动调用类中的构造函数(如果存在)。在构造函数中,可执行各种初始化操作,最常见的是创建实例属性。
Python在实例化时会检查是否实现了__init__方法。若未实现,则不对实例进行任何操作并返回对象;若已实现,则自动调用该方法,并自动传入self。这意味着在实例化传参时,我们无需理会self,直接传递给后续参数即可。
讲到属性,必须明确什么是属性。属性对象更像是一个变量,大多数对象(不包括Python的内置类型)都可以拥有属性,例如函数。
def Test(): pass Test.a = 123 print Test.a

因为函数本身也是一个对象。
属性在类中表现为一个变量,例如:
class NewClass(object): a = 123 print NewClass.a

当然,得益于Python的特性,我们可以在运行过程中为某个对象添加属性,而无需在类定义初期就写定。
注意,该方法应返回None,即通常不需要return任何对象,保持默认返回即可。
2. __new__(cls [,arg1,....]):
这也是一个构造器方法,属于类方法,在对Python的不可变数据类型进行继承扩展时较为常用。
以下是一段参考代码示例:
class RoundFloat(float):
def __new__(cls, val):
return super(RoundFloat, cls).__new__(cls, round(val, 2))
a = RoundFloat(3.14159)
print a

解构器方法
__del__(self [,arg1,....])
该方法将在对象的所有引用被清除后执行,例如:
class Test(object):
def __del__(self):
print '我被干掉了,兄弟们为我报仇!'
a = Test() # 创建了一个对象
b = a # b又引用了a
c = b # c又引用了b,现在 a 所指向的对象有3次引用,相当有三条命
del a # 干掉一条命
del b # 又干掉
del c # 听说你有3条命?全部干掉!

注意,这里仅输出了一次,说明直到最后引用计数归零时才执行删除。需注意以下几点:
1. 调用del并不意味着立即删除某个对象,仅是减少引用计数。
2. 若存在循环引用或其他原因导致实例引用未释放,该对象的__del__()可能永远不会被执行。
3. __del__()中未捕获的异常会被忽略(因为__del__()中使用的某些变量可能已被删除)。不要在__del__()中执行与实例无关的操作。
4. 一般情况下无需实现此方法,因为存在一定风险。
5. 若定义了__del__,且实例处于循环引用中,垃圾回收器将不会终止该循环——你需要自行显式调用del。
6. 若继承了父类,且父类中也定义了__del__,务必记得调用父类的__del__()。否则可能导致父类中的某些清理方法未被调用,引发不可预料的错误。
以上关于Python面向对象及类基本语法的讲解到此结束,希望能对大家有所帮助,欢迎支持本站。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









