python使用的总结Learning Python(三)

本文详细介绍了Python面向对象编程的基础知识,包括类、构造函数、析构函数、类成员函数、属性查找、操作符重载等核心概念,并通过实例展示了如何在Python中实现面向对象编程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


    停了好一段时间没去看Python,上周leader把一个别组leader写的Python工具给我看,叫我看懂后教大家怎么用,寒。这其实是一个用来 解析我们项目代码的工具,主要有一个XML格式object解析模块和一个txt格式code解析模块。还有一些辅助模块。
    唉,牛人写的就是不一样,刚开始看让我傻眼了好久。代码里面通篇的lambda表达式,还带多层嵌套的,最长的一个语句是一个嵌套lambda表达式和 map的return语句,大概有100多个字符我估计,还有自己从没接触过的__metaclass__,而所有代码里面基本没有几行注释,仅有的文档 是他自己写的一篇很短的关于我们项目code词法分析的说明(里面还一堆生词Learning <wbr>Python(三))。没有任何模块或者函数功能注释,更别谈语句的注释。当时我就觉得自己对python的了解是多么的肤浅。于是决定开始重新回顾一下python的一系列知识点,其中最重要的就是面向对象部分。其间google也好,手册也好,翻了不少。总算还了解了一些。正好写在这里用以备忘。
    其实很久前就想写了,只是觉得内容太多,自己懂得太少,组织能力又差,唉。写出来就算不害到小朋友,害到花花草草也是不好的啊。
    就从最简单的开始吧。
    说到面向对象,就离不开class,是的,在python里也一样。让我们这样定义一个最简单,最没用的class:
    class Test:
        pass
这真的是一个毫无用处的类,那么让我们给他添加一个成员函数吧:
    class Test:
        def MemberFunction(self):
            print "this is a test member function in %s" % self.__class__
你也许要问为什么有个参数叫self。嗯,这是python类中普通成员函数的特点。每一个普通成员函数的第一个参数都代表着调用这个函数的实例本身,也就是说,每个成员函数在定义的时候都必须至少有一个参数,用来传递实例。当然你也可以把它换成其它名字,me,instance之类随便,用self只是取其字面意思。你只要知道第一个参数代表的是调用函数的实例本身就够了。比如我们这样调用这个成员函数:
test = Test()
test.MemberFunction()
你会发现没有了self,是的,在调用的时候不需要再写self,因为self就是test,实际上在调用的时候python将其转为:
Test.MemberFunction(test)
也就是将test传给了self。我们将得到如下结果:
this is a test member function in __main__.Test
这仅仅是个连构造函数都没有的类,而构造,析构函数几乎是一个面向对象语言必需的特性。让我们来看看它们在Python里是什么样子的:
    class Test:
        def __init__(self):
            print "initializeing"
            self.x = 9
        def __del__(self):
            print "I am dying~"
这就是python的构造函数及析构函数(类似析构函数)。然后我们定义一个对象,并引用它的属性x,完了删除test实例
test = Test()
test.x
del test
我们将得到
9
I am dying
几乎没有什么surprise(注意:这里del只是把实例的引用计数减一,并不一定会调用__del__,只有引用计数真正到0的时候才会调用)。就像其它动态语言支持的一样,在python里面我们可以随意添加删除一个普通类实例的成员和属性。比如test.y = 9,那么test实例中将加入y这个属性。实际上在python里面,类的属性查找是用一个字典__dict__来保存的,也就是之前说过的dictionary。察看前面的test实例的__dict__将得到
{'x':9},是不是很方便?当然你也可以用dir(test)获得更多信息。
说到类成员,就不得不说Python类中的自定义操作,类似于操作符重载的机制。这里简单介绍一些基本的重载函数(记住其实是重写,python里面没有函数重载,就算是参数值不同的两个同名函数,也将被重写):
__new__(cls[,...]): 只用于new style class,一般用于生成一个cls的实例,然后返回给__init__使用,多数时候用于继承体系中。
 
__init__(self[,...]): 前面说过,构造函数
__del__(self): 类似析构函数
__repr__(self):在实例被用作内置函数repr的参数的时候调用,一般用于返回一个对象的"official string representation",就是说在适当环境下,可以用这个返回的string重新构造出一个具有相同值的python对象来。如果不能办到的话,应该返回一些有用的实例信息。
 
__str__(self):被内置函数str()调用,或者用于print函数,一般返回一个非正式的字符串,相对于repr来说。
__lt__(self, other):less than
__le__(self,other):less equal
__eq__(self,other):equal
__ne__(self,other):not equal
__gt__(self,other):greater than
__ge__(self,other):greater equal
以上6个是几个基本的比较操作符重载,这和C++中差不多,要注意的是它们之间并不存在任何隐式的内在联系,就是说A.__le__(B)为True不代 表A.__gt__(B)为False。所以为了让你的比较能够顺利进行,在你重载一个的同时,你应该将其对应的操作符也重载。你不一定要为这些函数返回 bool值,但是最终都会被转为bool。
 
__cmp__(self, other):如果以上六个操作符未被重载而被调用,那么__cmp__将作为第二个选择被调用。
 
__hash__(self):用于字典的key操作。就是说当对象作为dict的key时需要实现的函数。也可以用于被内置函数hash()调用。应该 返回一个32位的int值作为对象的唯一标识。要注意的是,两个对象__cmp__如果相等,那么它们的hash也应该相等。如果一个对象没有定义 __cmp__,那么它也不应该支持__hash__,如果只定义__cmp__而没有__hash__,对象还是不能用作一个字典的key。如果对象内 部定义了可变的对象并且实现了__cmp__,__eq__,那么也不应该实现__hash__,因为这将使字典的key可变,那是不对的。他们的关系比 较暧昧,值得考虑周到。
 
__nonzero__(self):用于对象的真值测试,也就是bool()函数。应该返回bool值。如果没有实现__nonzero__,那么__len__将作为备选调用,如果都没有实现,那么这个类的所有实例都被认为是True。
 
__getattr__(self, name):用于获取一个属性值,name就是所需属性的名字,相当于调用test.name。要注意的是,__getattr__只是在正常渠道没有找到属性时的备选函数,比如__dict__中未找到相关属性。在new style class中,有一个__getattribute__,可以完全控制对象属性的获取。
 
__setattr__(self, name, value):用于设定一个属性值,name是属性名字,value是要设定的值。相当于test.name = value。在旧式类型中,这相当于test.__dict__[name] = value。
 
__delattr__(self, name):用于从对象实例中删除一个属性。
 
__get__(self, instance, owner)
__set__(self, instance, value)
__delete__(self, instance)
以上三者只能用在new style class中, 他们其实是用于当一个类的实例出现在其他类的dict中时,对他们的取值赋值操作的自定义。比如:
class TestDescriptor(object):
     def __get__(self, obj, type=None):
         print "get", self, obj, type
         return self.data
     def __set__(self, obj, value):
         print "set", self, obj, value
         self.data = value
     def __delete__(self, obj):
         print "delete", self, obj
         del self.data
class UseDescriptor(object):
     desc = TestDescriptor()
我们这样用他们
>>> usedesc = UseDescriptor()
>>> usedesc.desc = 5
set <__main__.TestDescriptor object at 0x00DC8D50> <__main__.UseDescriptor object at 0x00DC8610> 5
>>> usedesc.desc
get <__main__.TestDescriptor object at 0x00DC8D50> <__main__.UseDescriptor object at 0x00DC8610> <class '__main__.UseDescriptor'>
5
>>> del usedesc.desc
delete <__main__.TestDescriptor object at 0x00DC8D50> <__main__.UseDescriptor object at 0x00DC8610>
一切便都明了:),当然你也可以试试不只是一个obj的情况,即对不同的对象获取不同的属性值。可以在TestDescriptor里面用一个dictionary,用obj作为下标来获取对应的值。
与此相对应的是用于包装类属性操作的property。类似于C#的attribute(仅用于new style class)。
something like this:
class TestProperty(object):
    def __init__(self): self.__x = None
    def getx(self): return self.__x
    def setx(self, value): self.__x = value
    def delx(self): del self.__x
    x = property(getx, setx, delx, "this is x")
简单地说,就是把__x包装一下,变成属性x,对x的操作实际都通过getx,setx等作用于__x身上。其中property的四个参数分别对应getter, setter, deleter和__doc__。实际的函数名字任取。是不是很好玩?:)
 
有时候你并不想你的类实例属性被动态增删,这时你可以考虑使用(new style class only):__slots__
一旦定义了这个属性,类实例的属性将固定为__slots__中的值,不能再被增删。
你可以这样用:
class TestSlots(object):
    __slots__ = ('only_me')
    def __getattr__(self, name):
        return "oh it's %s" % name
 

>>> testslot = TestSlots()
>>> testslot.x = 5

Traceback (most recent call last):
  File "<pyshell#103>", line 1, in <module>
    testslot.x = 5
AttributeError: 'TestSlots' object has no attribute 'x'

>>> testslot.only_me
"oh it's only_me"
>>> testslot.only_me = 5
>>> testslot.only_me
5

是不是很神奇?

注意一旦有了__slots__,__dict__将不复存在。不然就自相矛盾了不是吗 :)

 

我曾想总结一下所有能自定义的操作并列出来,发现实在是太多,无奈水平有限,只能将基本的陈列一下了(注意现在说的很多都只是python里面的classic class,即2.2版之前的所有user-defined class,然而2.2之后的new style class以后将占主导地位,应该更加重视,据说在3.0之后将完全取代old style class,后面将稍有介绍)。
接下来说说python的继承机制,在这之前,先说一个肯定会让你感到惊讶的特性,那就是python类的私有成员,你猜你将如何指定一个成员为私 有?private关键字?不,python里面没有这么一个关键字。Python的私有成员完全取决于它的命名:以__开头,不以__结尾的都被认为是 私有成员。是不是很奇怪?至少我觉得。所以对于大多数可以重写的函数__init__,__del__,__doc__等,都是共有的,但是不建议直接调 用。
(未完)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值