300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > 光棍节就要到了 要不要给你介绍个 Python 对象?| CSDN 博文精选

光棍节就要到了 要不要给你介绍个 Python 对象?| CSDN 博文精选

时间:2022-12-28 23:49:40

相关推荐

光棍节就要到了 要不要给你介绍个 Python 对象?| CSDN 博文精选

作者 | 天元浪子

责编| 刘静

出品 |CSDN 博客

前言光棍节就要到了,一说介绍对象,我猜你一定想到了派森大叔家的克蕾丝(class)小姐姐和黛夫(def)小哥哥。别想入非非了,严肃点儿!我们今天的的话题,不是介绍男女朋友,而是讲解如何面向对象编程,也就是程序员常说的OOP啦。 不知道前辈们为什么会把 Object Oriented Programming 翻译成面向对象编程,搞得单身程序员经常心猿意马地产生幻觉,以为屏幕上的俊男美女就是自己将来要面对的对象了。说到这里,我觉得还是台湾同行的计算机术语翻译得较为恰当。比如,cache,我们叫“缓存”,人家叫“快取”,音意俱佳。台湾同行把OPP翻译成“物件导向编程”——虽然同样不明觉厉,但至少可以让单身程序员暂时忘记没有“对象”的烦恼。 限盐少许,本博主正式开始为大家介绍对象。类和对象的概念学习面向对象编程,首先得搞明白,什么是对象?类是什么?实例化是什么意思?下图表达了我对OOP这几个基本概念的理解(实际上是妥协的结果——我和我的同事们讨论了很久,并翻墙参考了维基百科的说法)。 类是对我们要处理的客观事物的抽象。类用来描述具有相同的属性和方法的对象的集合,它定义了该集合中每个对象所共有的属性和方法。对象是类在内存的实例,一个类可以实例化为多个对象。类是抽象的,不占用内存,而对象是具体的,占用存储空间。类的成员作为Python初学者,大可不必把精力花费在令人费解的概念上,只需要掌握使用类的基本要素就可以了。未来的日子里,你有足够多的时间慢慢体会OOP的博大精深。随着经验的积累,OOP会自然而然地成为你的思维工具。 下面的代码,定义了一个名为A的类。所有的类,都有构造函数和析构函数,此外,还可以包含成员函数和成员变量。我喜欢把成员函数叫做类的方法,把成员变量叫做类的属性。

classA:def__init__(self):"""构造函数"""self.a=10#定义了一个成员变量adefgetA(self):"""成员函数"""print("a=%d"%self.a)def__del__():"""析构函数"""print("deleteobject")a=A()a.getA()

当类被实例化为对象时,首先执行构造函数,当对象被销毁时,会自动执行析构函数。一般的,我们会在构造函数中进行初始化工作,在析构函数中进行清理工作。

读到这里,有很多初学者一定会说:我定义类的时候,写过构造函数,但从没有写过析构函数,你为什么说所有的类都有构造函数和析构函数呢?没错,定义类的时候,即便我们不写构造函数和析构函数,这两个方法也照样存在(析构函数稍微有点特殊,我们不能直接看到它——除非是我们自己定义的)。如果我们自己定义了构造函数和析构函数,则将会取代系统自动赋予的这两个函数。下面的例子清晰地说明了其中的奥秘:类A既没有构造函数,也没有析构函数,类B只有析构函数,两个类都可以生成类实例,也都可以销毁,且 del b 时首先调用了自定义的析构函数。

>>>classA:pass>>>a=A()>>>dela>>>aTraceback(mostrecentcalllast):File"<pyshell#70>",line1,in<module>aNameError:name'a'isnotdefined>>>classB:def__del__(self):print('执行析构函数,清理现场')>>>b=B()>>>delb执行析构函数,清理现场>>>bTraceback(mostrecentcalllast):File"<pyshell#75>",line1,in<module>bNameError:name'b'isnotdefined

新式类和旧式类

在PY2中,类有新式类和旧式类两种。新式类需要继承自虚类Object,旧式类则不需要。PY2中类的写法有三种:

classA(object):新式类写法classA():旧式类写法classA:旧式类写法

在PY3中,只有新式类,不再支持旧式类。你如果习惯继承Object的写法,也完全没有问题。上面三种写法在PY3中都被解释成新式类。新式类和旧式类的主要区别是:

新式类可以继承Object的构造函数和析构函数,如果类的构造和析构函数没有特别的工作,可以省略。而旧式类则不能:

classA():defprint(self):print("IamA")classB(A):def__init__(self):A.__init__(self)b=B()

此时用PY2运行,会出现错误:AttributeError: class A has no attribute '__init__',使用PY3不会出现此错误。若改成新式类写法:

classA(object):defprint(self):print("IamA")classB(A):def__init__(self):A.__init__(self)b=B()

仍然用PY2运行,则都不会出错。

新式类可以使用super:

classA(object):defprint(self):print("IamA")classB(A):def__init__(self):super(A,self).__init__()b=B()

多重继承时,各父类的初始化和函数查找顺序不同:旧式类为深度优先继承,新式类为广度优先继承。

静态变量和实例变量在构造函数中定义的变量,我们称之为实例变量。实例变量只能在实例化后使用<对象名.变量名>的方式访问。静态变量一般定义在类的开始位置,独立于构造函数之外。静态变量既可以<对象名.变量名>的方式访问,也可以<类名.变量名>的方式访问。通常,类的静态变量一般用于保存类的静态属性,该属性可被类的方法使用,但不应该被类的方法修改。

>>>classA:static_x=10#静态变量def__init__(self):self.instance_y=5#实例变量>>>a=A()>>>a.static_x10>>>a.instance_y5>>>A.static_x10>>>A.instance_yTraceback(mostrecentcalllast):File"<pyshell#89>",line1,in<module>A.instance_yAttributeError:typeobject'A'hasnoattribute'instance_y'

静态函数与其他语音的静态函数不同,Python的静态函数有两种,都是用装饰器实现的:

classA:static_x=10def__init__(self):self.y=10@staticmethoddefstaticFuc():print(A.static_x)@classmethoddefclassFuc(cls):print(cls.static_x)A.staticFuc()A.classFuc()

Staticmethod 函数不能使用Self参数,因此不成访问任何成员变量,只能通过类名访问类的静态变量。

Classmethod 函数也不能使用Self参数,因此不成访问任何成员变量,但它有cls参数。cls参数不是对象的引用,而是类的引用,可以通过cls参数访问类的静态变量。面向对象三要素面向对象,有三大要素:继承、封装、多态。这里面概念非常多,往往越讲越糊涂。为了不至于误导读者,我尽可能不做解释,只给出例子,请自行揣摩。(1) 继承如果派生类只有一个父类,就是单继承。这是最常见的类定义形式。

classAnimal:defeat(self):print('我能吃')classBrid(Animal):def__init__(self):Animal.__init__(self)deffly(self):print('我会飞')brid=Brid()brid.eat()brid.fly()

如果派生类有多个父类,就是多继承。

classDeer:defshowHorns(self):print('我有鹿角')classHorse:defshowFace(self):print('我有马脸')classCow:defshowHoof(self):print('我有牛蹄')classDonkey:defshowTail(self):print('我有驴尾')classMilu(Deer,Horse,Cow,Donkey):#多继承派生出一个四不像类def__init__(self):Deer.__init__(self)Horse.__init__(self)Cow.__init__(self)Donkey.__init__(self)milu=Milu()milu.showHorns()milu.showFace()milu.showHoof()milu.showTail()

不管是单继承还是多继承,都可以在派生类中重写父类的函数——这叫做覆盖。

(2) 封装所谓封装,就是将类的成员变量、成员函数整合在一起,并对关键的信息进行保护或隐藏。信息保护或隐藏有三个级别:公有、保护、私有。如果你有C++的使用经验,我们先来回顾一下C++的信息隐藏规则:

公有成员:对类外部的任何代码可见;

保护成员:对类外部的任何代码都不可见,但对派生类可见;

私有成员:对类外部及派生类都不可见。

对应这三个级别,Python 是这样定义的: 以英文字母开头的成员为公有成员 以一个下划线开头的成员为保护成员 以两个下划线开关的成员为私有成员 下面我们试试 Python 的信息保护或隐藏规则是否有效。

>>>classA(object):def__init__(self,a,b,c):self.a=10#公有self._b=b#保护self.__c=c#私有defgetA(self):#公有returnself.adefsetA(self,a):#公有self.a=adefgetB(self):#公有returnself._bdef_setB(self,b):#保护self._b=bdefgetC(self):#公有returnself.__cdef__setC(self,c):#私有self.__c=c>>>a=A(10,20,30)>>>classB(A):pass>>>b=B(10,20,30)

试试访问公有成员:

>>>a.a10>>>a.getA()10>>>a.setA(5)>>>a.a5>>>b.a10>>>b.getA()10>>>b.setA(5)>>>b.a5

公有成员访问规则与C++相同。先跳过保护成员,看看私有成员:

>>>a.__cTraceback(mostrecentcalllast):File"<pyshell#85>",line1,in<module>a.__cAttributeError:'A'objecthasnoattribute'__c'>>>a.getC()30>>>a.__setC(5)Traceback(mostrecentcalllast):File"<pyshell#87>",line1,in<module>a.__setC(5)AttributeError:'A'objecthasnoattribute'__setC'>>>b.__cTraceback(mostrecentcalllast):File"<pyshell#88>",line1,in<module>b.__cAttributeError:'B'objecthasnoattribute'__c'>>>b.getC()30>>>b.__setC()Traceback(mostrecentcalllast):File"<pyshell#90>",line1,in<module>b.__setC()AttributeError:'B'objecthasnoattribute'__setC'

私有成员的访问规则也与C++相同。那我为什么跳过保护成员了?来试试吧:

>>>a._b20>>>a._setB(5)>>>a._b5

看到这里就已经不对了,应该只有类内部的代码和派生类能使用啊,怎么可以直接用了呢?是的,Python的保护成员访问规则与C++的确实不一样。那 Python 的保护成员是什么样的机制呢?原来,在 Python 的OOP中,保护成员公有成员没有任何区别。保护规则仅适用于 from xxx import * 这一种情况。

testA.py

classA(object):passclass_B(object):pass

testB.py

fromtestAimport*a=A()b=_B()

执行testB.py时:

Traceback(mostrecentcalllast):File"testB.py",line4,in<module>b=_B()NameError:name'_B'isnotdefined

此时,保护成员_B被保护了。但这种情况仅适用于from xxx import *这一种情况。如果testB.py这样写:testB.py

fromtestAimportA,_Ba=A()b=_B()

或者:

importtestAa=testA.A()b=testA._B()

则是没有任何问题的。(3) 多态当父类有多个派生类,且派生类都实现了同一个成员函数,则可以实现多态:

classH2O(object):defwhat(self):print("IamH2O")classWater(H2O):defwhat(self):print("Iamwater")classIce(H2O):defwhat(self):print("Iamice")classWaterVapor(H2O):defwhat(self):print("Iamwatervapor");defwhat(obj):obj.what()objs=[H2O(),Water(),Ice(),WaterVapor()]forobjinobjs:what(obj)

抽象类抽象类不能被实例化,只能作为父类被其它类继承,且派生类必须实现抽象类中所有的成员函数。抽象类应用场景是什么呢?我曾经做过很多下载数据的脚本插件,不同的数据源使用不同的脚本,所有这些脚本要求必须有名字相同的方法,此时,抽象类就派上用场了。

>>>importabc>>>classA(object,metaclass=abc.ABCMeta):@abc.abstractmethoddefa(self):pass@abc.abstractmethoddefb(self):pass>>>classC(A):defa(self):print("a")>>>c=C()Traceback(mostrecentcalllast):File"<pyshell#127>",line1,in<module>c=C()TypeError:Can'tinstantiateabstractclassCwithabstractmethodsb

单例模式

单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时(如软件配置类,无论在软件的什么地方实例化,永远都是那一个对象),单例模式就能派上用场。比如,Python 日志模块中的日志对象,或者异步通讯框架Twisted 里面的反应堆(reactor),都是典型的单例模式——尽管它们不一定是下面这种方法实现的。 Python可以使用装饰器的方法使用单例模式:

defSingleton(cls):_instance={}def_singleton(*args,**kargs):ifclsnotin_instance:_instance[cls]=cls(*args,**kargs)return_instance[cls]return_singleton@SingletonclassConfig(object):passcfg1=Config()cfg2=Config()print(cfg1iscfg2)

后记

行文至此,说几句题外话。CSDN 不止为我们提供了这样一个交流平台,还经常推出各类技术交流活动。近期我将在 GeekTalk 栏目,和 Python 新手共同探讨如何快速成长为基础扎实、功力强大的程序员。CSDN 还为这个活动提供了一些纪念品。如果有兴趣,请扫码加入: 版权声明:本文为CSDN博主「天元浪子」的原创文章。想为博主点赞?想要请教博主?扫描下方二维码,快速获取与博主直面沟通的方式吧!

【END】

Python学习方法Python入门须知!(内附python教程分享)

/topic/python115?utm_source=csdn_bw

热 文推 荐

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。