python完全支持面向对象编程_[Python] 类与面向对象编程
1. class语句
类通常是由函数、变量和属性组成的集合。使用class语句可以定义类,例如:
class Account(object):
num_accounts = 0
def __init__(self, name, balance):
self.name = name
self.balance = balance
Account.num_accounts += 1
def __del__(self):
Account.num_accounts -= 1
def deposit(self, amt):
self.balance = self.balance + amt
def withdraw(self, amt):
self.balance = self.balance - amt
def inquiry(self):
return self.balance
在类主体执行期间创建的值放在类对象中,这个对象充当着命名空间,例如:
Account.num_accunts
Account.__init__
Account.__del__
Account.deposit
Account.withdraw
Account.inquiry
需要注意的是,class语句本身并不创建该类的任何类型。类仅设置将在以后创建的所有实例都使用的属性。类中定义的函数称为实例方法。类的实例作为第一个参数传递,根据约定,这个参数称为self,但所有合法的标识符都可以使用。类变量是可在类的所有实例之间共享的值。比如上例的num_accounts变量用于跟踪存在多少个Account实例。
2. 类实例
类的实例是以函数形式调用类对象来创建的。这种方法将创建一个新实例,而后将该实例传递给类的__init__()方法。__init__()方法的参数包括新创建的实例self和在调用类对象时提供的参数。例如:
a = Account("Guido", 1000.00)
b = Account("Bill", 10.00)
通过将属性分配给self来将其保存到实例中。例如self.name = name表示将name属性保存在衫例中。使用"."运算符可以访问这些属性以及类属性,例如:
a.deposit(100.00)
b.withdraw(50.00)
name = a.name
尽管类会定义命名空间,但它们不会为在方法体内使用的名称限定范围。所以在实现类时,对属性和方法的引用必须是完全限定的。比如之前的例子中使用的是self.balance而非balance。如果希望从一个方法中调用另一个方法,也可以采用这种方式,例如:
class Foo(object):
def bar(self):
print("bar!")
def spam(self):
bar(self) # 错误,抛出NameError异常
self.bar()
Foo.bar(self)
3. 继承
继承是一种创建新类的机制。原始类称为基类或超类。新类称为派生类或子类。通过继承创建类时,所创建的类将“继承”其基类定义的属性。派生类可以重新定义属性并添加自己的属性。
在class语句中使用以逗号分隔的基类名称列表来指定继承。如果没有有效的基类,将继承object。继承通常会重新定义现有方法的行为,例如:
import random
class EvilAccount(Account):
def inquiry(self):
if andom.randint(0, 4) == 1:
return self.balance * 1.10
else:
return self.balance
c = EvilAccount("George", 1000.00)
c.deposit(10.0)
available = c.inquiry()
如果搜索一个属性时未在实例或实例的类中找到匹配项,搜索将会在基类上进行。这个过程会一直继续下去,直到没有更多的基类可供搜索。子类可以定义自己的__init__()方法。因此,要由派生类调用基类的__init__()方法来对它们进行恰当的初始化。如果基类未定义__init__(),就可以忽略这一步。如果不知道基类是否定义了__init__(),可在不提供任何参数的情况下调用它,因为始终存在一个不执行任何操作的默认__init__()实现。例如:
class EvilAccount(Account):
def __init__(self, name, balance, evilfactor):
Account.__init__(self, name, balance)
self.evilfactor = evilfactor
def inquiry(self):
if random.randint(0, 4) == 1:
return self.balance * self.evilfactor
else:
return self.balance
有时,派生类重新实现了方法,但是还想调用原始的实现,可以将实例self作为第一个参数传递,例如:
class MoreEvilAccount(EvilAccount):
def deposit(self, amount):
self.withdraw(5.00)
EvilAccount.deposit(self, amount)
但是这种写法容易引起一些混淆,可以使用另一种方案,用super()函数,例如:
class MoreEvilAccount(EvilAccount):
def deposit(self, amount):
self.withdraw(5.00)
super().deposit(amount)
Python支持多重继承,通过让一个类列出多个基类即可指定多重继承,例如:
class DepositCharge(object):
fee = 5.00
def deposit_fee(self):
print(self.fee)
class WithdrawCharge(object):
fee = 2.50
def withdraw_fee(self):
print(self.fee)
class MostEvilAccount(EvilAccount, DepositCharge, WithdrawCharge):
def deposit(self, amt):
self.deposit_fee()
super().deposit(amt)
def withdraw(self, amt):
self.withdraw_fee()
super().withdraw(amt)
withdraw_fee()实际并未使用在自己的类中初始化的fee值。属性fee是在两个不同的基类中定义的类变量,程序使用了其中一个。要找到使用了多重继承的属性,可以在列表中对所有基类按从“最特殊”的类到“最不特殊”的类。而后在搜索属性时,就会按这个顺序搜索列表,直至找到该属性的第一个定义。对于任何给定的类,通过打印它的__mro__属性即可查看基类的顺序。
4. 静态方法和类方法
静态方法是一种普通函数,就位于类定义的命名空间中。要定义静态方法,可使用@staticmethod装饰器,例如:
class Foo(object):
@staticmethod
def add(x, y):
return x + y
要调用静态方法,只需用类名作为它的前缀,例如:
x = Foo.add(3, 4)
类方法是将类本身作为对象进行操作的方法。类方法使用@classmethod装饰器定义,根据约定,类是作为第一个参数(名为cls)传递的,例如:
class Times(object):
factor = 1
@classmethod
def mul(cls, x):
return cls.factor * x
class TwoTimes(Times):
factor = 2
x = TwoTimes.mul(4)
5. 特性
特性是一种特殊的属性,访问它时会计算它的值,例如:
class Circle(object):
def __init__(self, radius):
self.radius = radius
@property
def area(self):
return math.pi * self.radius ** 2
@property
def perimeter(self):
return 2 * math.pi * self.radius
在这个例子中,Circle实例存储了一个实例变量c.radius。c.area和c.perimeter是根据该值计算得来的。@property装饰器支持以简单属性的形式访问后面的方法,无需添加额外的()来调用该方法。方法本身是作为一类特性被隐式处理的,当创建一个实例然后访问实例的方法时,不会返回原始函数对象,会得到绑定方法。绑定方法是一个对象,表示将在对象中调用()运算符时执行的方法调用。这种绑定方法对象是由在后台执行的特性函数静默创建的。使用@staticmethod和@classmethod定义静态方法和类方法时,实际上就指定了使用不同的特性函数。
特性还可以拦截操作,以设置和删除属性。这是通过向特性附加其他setter和deleter方法来实现的,例如:
class Foo(object):
def __init__(self, name):
self.__name = name
@property
def name(self):
return self.__name
@name.setter
def name(self, value):
if not isinstance(value, str):
raise TypeError("Must be a string!")
self.__name = value
@name.deleter
def name(self):
raise TypeError("Can't delete name")
f = Foo("Guido")
n = f.name
f.name = "Monty" # 调用setter
f.name = 45 # 调用setter(TypeError)
del f.name
6. 描述符
使用特性后,对属性的访问将由一系列用户定义的get、set和delete函数控制。这种属性控制方式可以通过描述符对象进一步推广。描述符就是一个表示属性值的对象,通过实现一个或多个特殊的__get__()、__set__()和__delete__()方法,可以将描述符与属性访问机制挂钩,例如:
class TypedProperty(object):
def __init__(self, name, type, default=None):
self.name = "_" + name
self.type = type
self.default = default if default else type()
def __get__(self, instance, cls):
return getattr(instance, self.name, self.default)
def __set__(self, instance, value):
if not isinstance(value, self.type):
raise TypeError("Must be a %s" % self.type)
setattr(instance, self.name, value)
def __delete__(self, instance):
raise AttributeError("Can't delete attribute")
class Foo(object):
name = TypedProperty("name", str)
num = TypedProperty("num", int, 42)
在这个例子中,类TypedProperty定义了一个描述符,分配属性时它将进行类型检查,例如:
f = Foo()
a = f.name # 隐式调用Foo.name.__get__(f.Foo)
f.name = "Guido" # 调用Foo.name.__set__(f, "Guido")
del f.name # 调用Foo.name.__delete__(f)
7. 数据封装和私有属性
默认情况下,类的所有属性和方法都是“公共的”。这意味着对它们的访问没有任何限制。在基类中的所有内容都会被 派生类继承,并可从派生类内进行访问。这可能导致在派生类中定义的对象与在基类中定义的对象之间发生命名空间冲突,为了解决该问题,类中所有以双下划线开头的名称都会变成具有_类名__Foo形式的新名称。例如:
class A(object):
def __init__(self):
self.__X = 3 # 变为self._A__X
def __spam(self): # 变成_A__spam()
pass
def bar(self):
self.__spam() # 只调用A.__spam()
class B(A):
def __init__(self):
A.__init__(self)
self.__X = 37 # 变为self._B__X
def __spam(self): # 变为_B__spam()
pass
这种方案似乎隐藏了数据,但并没有严格的机制来实际阻止对类的“私有”属性进行访问。尽管这种变形似乎是一个额外的处理步骤,但变形过程实际上只在定义类时发生一次。而且,名称变形不会在getattr()、hasattr()、setattr()或delattr()等函数中发生,因为在这些函数中,属性名称指定为字符串。对于这些函数,需要显示使用变形名称。
8. 对象表示和属性绑定
在类的内部,实例是使用字典来实现的,可以用实例的__dict__属性的形式访问该字典。这个字典包含的数据对每个实例而言都是唯一的。对实例的修改始终会反映到局部__dict__属性中。同样,如果直接对__dict__进行修改,所做的修改也会反映在该属性中。
实例被特殊属性__class__链接回它们的类。在特殊属性__base__中将类链接到它们的基类。只要使用obj.name = value设置属性,就会调用特殊方法obj.__setattr__("name", value)。如果使用del obj.name删除了一个属性,就会调用特殊方法obj.__delattr__("name")。
来查找属性时,将调用特殊方法obj.__getattrribute__("name")。如果搜索过程失败,最终会尝试调用类的__getattr__()方法(如果已定义)来查找该属性。如果这也失败,就会抛出AttributeError异常。
9. 运算符重载
用户可以定义Python的所有内置运算符,比如,如果希望向Python添加一种新的数字类型,可以定义一个类并在该类中定义__add__(),例如:
class complex(object):
def __init__(self, real, imag=0):
self.real = float(real)
self.imag = float(imag)
def __repr__(self):
return "Complex(%s, %s)" % (self.real, self.imag)
def __str__(self):
return "(%g+%gj)" % (self.real, self.imag)
def __add__(self, other):
return Complex(self.real + other.real, self.imag + other.imag)
def __sub__(self, other):
return Complex(self.real - other.real, self.imag - other.imag)
在这个例子中,__repr__()方法创建一个字符串,可以计算该字符串来重新创建对象。__str__()方法创建具有良好输出格式的字符串。__add__()和__sub__()实现数学运算。
10. 抽象基类
要定义抽象基类,需要使用abc模块。该模块定义一个元类(ABCMeta)和一组装饰器,可以按如下方式使用:
from abc import ABCMeta, abstractmethod, abstractproperty
class Foo:
__metaclass__ = ABCMeta
@abstractmethod
def spam(self, a, b):
pass
@abstractproperty
def name(self):
pass
要定义抽象类,需要将其元类如上所示设置为ABCMeta。因为抽象类的实现离不开元类。在抽象类中,@abstractmethod和@abstractproperty装饰器指定Foo的子类必须实现一个方法或特性。抽象类不能直接实例化。这一限制也适用于派生类,如果派生类没有实现一个或多个抽象方法,那么尝试创建派生类实例将会失败。
抽象基类支持对已经存在的类进行注册,使其属于该基类。这是用register()方法完成的,例如:
class Grok(object):
def spam(self, a, b):
print("Grok.spam")
Foo.register(Grok)
10. 元类
在Python中定义类时,类定义本身将成为一个对象。例如:
class Foo(object): pass
isinstance(Foo, object) # True
类对象的这种创建方式是由一种名为元类的特殊对象控制的。即元类就是知道如何创建和管理类的对象。如果查看Foo的类型,将会发现它的类型为type。
使用class语句定义新类时,类主体将作为其自己的私有字典内的一系列语句来执行。语句的执行与正常代码执行过程相同,只是会在私有成员上发生名称变形。最后,类的名称、基类列表和字典将传递给元类的解构函数,以创建相应的类对象。类创建的最后一步,也就是调用元类type()的步骤,可以自定义。
类可以显式地指定其元类,这通过在基类元组中提供metaclass关键字参数来实现,例如:
class Foo(metaclass=type)
__metaclass__ = type
...
如果没有显示指定元类, class语句将检查基类元组中的第一个条目。在这种情况下,元类与第一个基类的类型相同。如果没有指定基类,class语句将检查全局变量__metaclass__是否存在。如果找到了该变量,将使用它来创建类。如果没有找到任何__metaclass__值,Python将使用默认的元类(type())。
10. 类装饰器
类装饰器是一种函数,它接受类作为输入并返回类作为输出,例如:
registry = { }
def register(cls):
registry[cls.__clsid__] = cls
return cls
要使用该函数,可以在类定义前将它用作装饰器,例如:
class Foo(object):
__clsid__ = "123-456"
def bar(self):
pass
等同的方式如下:
class Foo(object):
__clsid__ = "123-456"
def bar(self):
pass
register(Foo)
python完全支持面向对象编程_[Python] 类与面向对象编程相关推荐
- 声明式编程与函数式编程_实用程序类与函数式编程无关
声明式编程与函数式编程 最近,我被指控反对函数式编程,因为我将实用程序类称为反模式 . 绝对是错的! 好吧,我确实认为它们是一个糟糕的反模式,但是它们与函数式编程无关. 我相信有两个基本原因. 首先, ...
- Java Review - 并发编程_原子操作类LongAdder LongAccumulator剖析
文章目录 概述 小Demo 源码分析 重要的方法 long sum() reset sumThenReset longValue() add(long x) longAccumulate(long x ...
- python装饰器和异常处理_装饰器异常处理-面向对象编程-Python教程自动化开发_Python视频教程...
课程目标:使学员掌握python基础教程知识,能够开发常用的PYTHON脚本 课程特色:实战,细致,生动,深入浅出 适用人群:适合PYTHON初学者 课程详情:该Python视频教程从0基础开始讲解P ...
- python写代码没思路_如何训练自己的编程思路
最近一个周末班的学员,问黄哥你在讲解编程思路训练时,如何想到这个思路的. 首先要解决一个问题,学习编程是学习啥?是纯学习语法吗? 不是,是要学习计算思维,编程思路. 何为计算思维: 计算思维(Comp ...
- Python代码列主元消去法matlab编程_工业机器人用什么语言编程的?
曾经有很多小伙伴一直问,工业机器人编程用的是什么语言啊?这次给大家总结一下机器人编程中常用的语言. 1.硬件描述语言(HDLs) 硬件描述语言一般是用来描述电气的编程方式.这些语言对于一些机器人专家来 ...
- python 读取鼠标选中文本_木辛老师的编程课堂:Python和Qt之页面布局实战篇(一)...
通过前几节课的学习,我们已经基本上掌握了使用Qt Designer完成简单的布局管理.通过这些知识的学习,我们算是对PyQt进行了初步的了解,也算是入门了! 但是仅仅掌握这些知识还是远远不够的: 高深 ...
- 在下列数据类型中、python不支持的是_在下列数据类型中, Python不支持的是_学小易找答案...
[填空题]如果想测试变量的类型,可以使用 __________关键字 来实现. [单选题]如在类中存在有_value,则表示它是 [填空题]当运行测试输入6789时,写出下面 Python程序的执行结 ...
- python莱布尼茨法计算π_酷叮猫少儿编程讲堂——Python 用莱布尼茨等式求π
原标题:酷叮猫少儿编程讲堂--Python 用莱布尼茨等式求π Python 用莱布尼茨等式求π 2018-08-01 德国大数学家莱布尼茨Leibniz在研究圆周率π的过程中发现一个数学公式是这样的 ...
- python视窗编程_[PYTHON] 核心编程笔记(19.图形用户界面编程)
19.1 简介 19.1.1 什么是Tcl,Tk和Tkinter? 19.1.2 安装和使用Tkinter # apt-get install python-tk -y # python ------ ...
最新文章
- 直播卡顿原因详解及优化
- Java私有变量是否可继承
- python的第三方库-Python 的第三方库到底行不行啊?
- spring循环依赖及解决方法
- 【嵌入式】Libmodbus源码分析(一)-类型和结构体
- Vue获取DOM元素的属性值
- Java中的文件压缩
- c语言累积乘,C语言编程累积2
- endnote layout can not be formatted because it is no longer open
- ramda 为 占位符 添加类型
- 这些屏幕特效是咋实现的
- 计算机语言安装不上,安装程序包的语言不受系统支持该怎么办?解决方法教程...
- python cookbook 读书笔记2(字符串处理2)
- 用清水洗手和肥皂、洗手液等洗手的区别???
- 产品设计和交互设计总结
- Python:用【Win32】模块,结合Word通配符替换,将Word文档表格的部分内容替换为加粗
- 视频系统部署 kvs
- 5.03GEN-B发布!PSP 2000v3/3000最新自制系统
- 易飞ERP和钉钉办公集成——ERP移动审批解决方案
- 文档生成工具-Doxygen使用方法以及注释规则
热门文章
- c语言首尾指针相同 则,6.C语言指针练习题.doc
- [GKCTF2020]Pokémon
- Python中type()详解:动态创建类
- Python十大常用文件操作
- python3中使用subprocess模块执行外部命令
- python中json.dump() 和 json.dumps() 有那些区别?
- 170 道 Python 爬虫面试题(2019 版)
- linux驱动双摄像头,详解linux 摄像头驱动编写
- 放置奇兵 新 粉石墨
- python如何读取一个文件夹下的多个文件(夹)?