Python学习--11 面向对象高级编程
多重继承
Python里允许多重继承,即一个类可以同时继承多个类:
class Mammal(Animal):
pass
class Runnable(object):
def run(self):
print('Running...')
class Dog(Mammal, Runnable):
pass
这样,Dog
同时拥有Mammal
、Runnable
的属性和方法。
__slots__限制实例的属性
由于类的实例可以动态绑定新的属性,有时候我们不希望这样,可以通过__slots__
进行限制:
class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
然后,我们试试:
>>> s = Student() # 创建新的实例
>>> s.name = 'yjc' # 绑定属性'name'
>>> s.age = 25 # 绑定属性'age'
>>> s.score = 99 # 绑定属性'score'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'score'
由于score
没有被放到__slots__
中,所以不能绑定score属性,试图绑定score将得到AttributeError
的错误。
使用__slots__
要注意,__slots__
定义的属性仅对当前类实例起作用,对继承的子类是不起作用的:
>>> class SubStudent(Student):
... pass
...
>>> g = SubStudent()
>>> g.score = 99
除非在子类中也定义__slots__
,这样,子类实例允许定义的属性就是自身的__slots__
加上父类的__slots__
。
@property装饰器
当我们通过实例使用类的属性时,通常不希望直接访问,而是处理之后再暴露出来。例如:
class Student(object):
def setScore(self, value):
if(value > 100):
value = 100
if(value < 0):
value = 0
self.__score = value
def getScore(self):
return self.__score
s = Student()
s.setScore(199)
print(s.getScore())
输出:
100
这里,__score
属性我们通过setScore
先设置,然后使用getScore
获得,并对不合理值进行了处理。
上面我们通过类里的方法实现了类属性的设置和访问。那么,有没有既能检查参数,又可以用类似属性这样简单的方式来访问类的变量呢?Python里的@property
装饰器就是做这个的:
class Student(object):
@property
def score(self):
return self.__score
@score.setter
def score(self, value):
if(value > 100):
value = 100
if(value < 0):
value = 0
self.__score = value
s = Student()
s.score = 199
print(s.score)
输出:
100
Python内置的@property
装饰器就是负责把一个方法变成属性调用。此时,@property
本身又创建了另一个装饰器@score.setter
,负责把一个setter
方法变成属性赋值。
@property
广泛应用在类的定义中,可以让调用者写出简短的代码,同时保证对参数进行必要的检查,这样,程序运行时就减少了出错的可能性。
定制类
我们可以使用类似__slots__
这种变量或者函数名来定制类,这些在Python里是有特殊作用的。
通过自定义下面这些属性或方法,我们可以对类做自定义处理:
__slots__
:限制实例的属性
__len__()
:自定义返回长度
__str__()
:当尝试使用print打印类的时候,自定义返回类的内容。因为默认打印出一堆<__main__.Student object at 0x109afb190>
,不好看。
class Student(object):
def __init__(self, name):
self.name = name
def __str__(self):
return 'Student object (name: %s)' % self.name
print(Student('yjc'))
输出:
Student object (name: yjc)
这样打印出来的实例,不但好看,而且容易看出实例内部重要的数据。
__repr__()
:与__str__()
类似,当直接敲变量Student('yjc')
不用print的时候,会自动调用该方法。
__getattr__()
:默认调用类里不存在的属性时,会报错。通过该方法,可以动态返回一个属性。
class Student(object):
def __init__(self):
self.name = 'yjc'
def __getattr__(self, attr):
if attr=='score':
return 80
这时候调用score属性,不会报错了:
>>> s = Student()
>>> s.name
'yjc'
>>> s.score
80
__call__()
:通过覆写该方法,可以将实例像方法那样直接调用:
class Student(object):
def __init__(self, name):
self.name = name
def __call__(self):
print('My name is %s.' % self.name)
调用方式如下:
>>> s = Student('yjc')
>>> s() # self参数不要传入
My name is yjc.
__call__()
还可以定义参数。对实例进行直接调用就好比对一个函数进行调用一样,所以你完全可以把对象看成函数,把函数看成对象,因为这两者之间本来就没啥根本的区别。
通过callable()
函数,我们就可以判断一个对象是否是可调用对象:
>>> callable(Student())
True
>>> callable(max)
True
枚举类
Python提供Enum
类来实现枚举功能:
# coding: utf-8
from enum import Enum
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
# 可以直接使用Month.Jan来引用一个常量:
print(Month.Jan.value)
# 枚举所有成员:
for name,member in Month.__members__.items():
print(name, '=>', member, ',', member.value)
输出:
1
Jan => Month.Jan , 1
Feb => Month.Feb , 2
Mar => Month.Mar , 3
Apr => Month.Apr , 4
May => Month.May , 5
Jun => Month.Jun , 6
Jul => Month.Jul , 7
Aug => Month.Aug , 8
Sep => Month.Sep , 9
Oct => Month.Oct , 10
Nov => Month.Nov , 11
Dec => Month.Dec , 12
value
属性则是自动赋给成员的int
常量,默认从1开始计数。
如果想自定义value值:
# coding: utf-8
from enum import Enum,unique
@unique
class Month(Enum):
Jan = 0
Feb = 1
Mar = 2
print(Month.Jan.value)
for name,member in Month.__members__.items():
print(name, '=>', member, ',', member.value)
输出:
0
Jan => Month.Jan , 0
Feb => Month.Feb , 1
Mar => Month.Mar , 2
@unique
装饰器可以帮助我们检查保证没有重复值。如果重复了,会报ValueError错误:
ValueError: duplicate values found in <enum 'Month'>
元类
动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运行时动态创建的。
type()
type()
可以查看一个类型或变量的类型:
# coding:utf-8
class Hello(object):
pass
h = Hello()
print(type(h))
print(type(Hello))
print(type(object))
输出:
<class '__main__.Hello'>
<class 'type'>
<class 'type'>
通过打印我们发现,类Hello
的类型是type
,但它的实例类型是class Hello
类型。
Python里class的定义是运行时动态创建的,而创建class的方法就是使用
type()
函数。
type()
函数既可以返回一个对象的类型,又可以创建出新的类型,比如,我们可以通过type()
函数创建出Hello
类:
# 定义成员方法:减
def sub(self, x, y):
return x-y
# 生成类
Hello = type('Hello', (object,), {"add":add, "mysub":sub})
h = Hello()
print(h.add(1, 2))
print(h.mysub(1, 2))
print(type(h))
print(type(Hello))
输出:
3
-1
<class '__main__.Hello'>
<class 'type'>
要创建一个class对象,type()
函数依次传入3个参数:
- class的名称;
- 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
- class的方法名称与函数绑定。
通过type()
函数创建的类和直接写class是完全一样的,因为Python解释器遇到class定义时,仅仅是扫描一下class定义的语法,然后调用type()
函数创建出class。
metaclass
metaclass
,直译为元类,可以理解为类的模板。通过metaclass
也可以动态创建类。
metaclass
是Python面向对象里最难理解,也是最难使用的魔术代码。正常情况下,我们不会碰到需要使用metaclass
的情况。
通过metaclass
创建出类,需要:先定义metaclass
,然后创建类。下面的示例是给自定义的MyList
增加一个add
方法:
示例:
# coding: utf-8
# metaclass是类的模板,所以必须从`type`类型派生:
class ListMetaclass(type):
def __new__(cls, name, base, attrs):
attrs['add'] = lambda self,value: self.append(value)
#打印参数信息
print(cls, '\n', name, '\n', base, '\n', attrs, '\n')
return type.__new__(cls, name, base, attrs)
# 根据metaclass产生类
class MyList(list, metaclass=ListMetaclass):
pass
# 类继承
class OtherList(MyList):
pass
L = MyList()
L.add('3')
print(L)
输出:
<class '__main__.ListMetaclass'>
MyList
(<class 'list'>,)
{'add': <function ListMetaclass.__new__.<locals>.<lambda> at 0x02424540>, '__module__': '__main__', '__qualname__': 'MyList'}
<class '__main__.ListMetaclass'>
OtherList
(<class '__main__.MyList'>,)
{'add': <function ListMetaclass.__new__.<locals>.<lambda> at 0x02424588>, '__module__': '__main__', '__qualname__': 'OtherList'}
['3']
以上通过metaclass
动态生成了MyList
类,并增加了成员方法add()
。
通过分析输出,我们可以发现:__new__()
方法接收到的参数依次是:
- 当前准备创建的类的对象,例如
ListMetaclass
; - 类的名字,例如
MyList
; - 类继承的父类集合,例如
list
; - 类的方法集合,例如
add
、__module__
、__qualname__
。
什么时候需要用到metaclass
呢?ORM就是一个典型的例子。
Python学习--11 面向对象高级编程的更多相关文章
- python学习笔记--面向对象的编程和类
一.面向对象的编程 面向对象程序设计--Object Oriented Programming,简称oop,是一种程序设计思想.二.面向对象的特性类:class类,对比现实世界来说就是一个种类,一个模 ...
- python基础之面向对象高级编程
面向对象基本知识: 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用 类 是一个模板,模板中包装了多个"函数"供使用(可以讲多函数中公用的变量封装到对象中) ...
- Python基础——7面向对象高级编程
实例与类动态添加方法 实例添加属性: def Student(object): pass s = Student() s.name = ‘syz’ 实例添加方法 from types import M ...
- Python学习之==>面向对象编程(二)
一.类的特殊成员 我们在Python学习之==>面向对象编程(一)中已经介绍过了构造方法和析构方法,构造方法是在实例化时自动执行的方法,而析构方法是在实例被销毁的时候被执行,Python类成员中 ...
- Hadoop学习笔记(7) ——高级编程
Hadoop学习笔记(7) ——高级编程 从前面的学习中,我们了解到了MapReduce整个过程需要经过以下几个步骤: 1.输入(input):将输入数据分成一个个split,并将split进一步拆成 ...
- python学习之路网络编程篇(第四篇)
python学习之路网络编程篇(第四篇) 内容待补充
- C++面向对象高级编程(九)Reference与重载operator new和operator delete
摘要: 技术在于交流.沟通,转载请注明出处并保持作品的完整性. 一 Reference 引用:之前提及过,他的主要作用就是取别名,与指针很相似,实现也是基于指针. 1.引用必须有初值,且不能引用nul ...
- C++面向对象高级编程(八)模板
技术在于交流.沟通,转载请注明出处并保持作品的完整性. 这节课主要讲模板的使用,之前我们谈到过函数模板与类模板 (C++面向对象高级编程(四)基础篇)这里不再说明 1.成员模板 成员模板:参数为tem ...
- C++面向对象高级编程(七)point-like classes和function-like classes
技术在于交流.沟通,转载请注明出处并保持作品的完整性. 1.pointer-like class 类设计成指针那样,可以当做指针来用,指针有两个常用操作符(*和->),所以我们必须重载这两个操作 ...
随机推荐
- 通用的进程监控脚本process_monitor.sh使用方法
不用做任何修改,即可用process_monitor.sh监控各种进程. 源码下载:https://github.com/eyjian/libmooon/blob/master/shell/proce ...
- Sql递归关联情况,With作为开头条件。
with Test_Recursion(Id,ParentId)AS(select Id,ParentId from [V_KPI_DetailsActivities] where ParentId ...
- AlexNet详解
在imagenet上的图像分类challenge上Alex提出的alexnet网络结构模型赢得了2012届的冠军.要研究CNN类型DL网络模型在图像分类上的应用,就逃不开研究alexnet,这是CNN ...
- 集成算法(chapter 7 - Hands on machine learning with scikit learn and tensorflow)
Voting classifier 多种分类器分别训练,然后分别对输入(新数据)预测/分类,各个分类器的结果视为投票,投出最终结果: 训练: 投票: 为什么三个臭皮匠顶一个诸葛亮.通过大数定律直观地解 ...
- Hive Bug修复:ORC表中array数据类型长度超过1024报异常
目前HVIE里查询如下语句报错: select * from dw.ticket_user_mtime limit 10; 错误如下: 17/07/06 16:45:38 [main]: DEBUG ...
- Asp .Net core 2 学习笔记(3) —— 静态文件
这个系列的初衷是便于自己总结与回顾,把笔记本上面的东西转移到这里,态度不由得谨慎许多,下面是我参考的资源: ASP.NET Core 中文文档目录 官方文档 记在这里的东西我会不断的完善丰满,对于文章 ...
- Java内存模型与共享变量可见性
此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 注:本文主要参考自<深入理解Java虚拟机(第二版)>和<深入理解Java内存模型> ...
- ArrayBlockingQueue源码解析(2)
此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 3.3.public void put(E e) throws InterruptedException 原 ...
- 2018 Multi-University Training Contest 6
A.oval-and-rectangle 题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=6362 题意:在长半轴为a,短半轴为b的椭圆内部,以y=c( ...
- 伪装为 吃鸡账号获取器 的QQ木马分析
本文作者:i春秋作家坏猫叔叔 0×01 起因随着吃鸡热潮的来临,各种各样的吃鸡辅助和账号交易也在互联网的灰色地带迅速繁殖滋生.其中有真有假,也不乏心怀鬼胎的“放马人”.吃过晚饭后在一个论坛看到了这样一 ...