面向对象设计中最基础的3个概念:数据封装、继承和多态

动态给class增加功能

正常情况下,当定义了一个class,然后创建了一个class的实例后,可以在程序运行的过程中给该实例绑定任何属性和方法,这就是动态语言的灵活性

(1)给一个实例绑定的方法,对另一个实例是不起作用的。

(2)为了给所有实例都绑定方法,可以给class绑定方法。

Student.set_age=set_age

注:是set_age,而不是set_age(  )

(3) 给class绑定方法后,所有实例均可调用。

 #定义了Student类
class Student(object):
pass #给实例s添加name属性
s=Student()
s.name="Jane"
print("s.name:",s.name) #给实例绑定一个方法
def set_age(self,age):
self.age=age from types import MethodType s.set_age=MethodType(set_age,s) s.set_age(23)
print("s.age:",s.age) #print("注意使用的是set_age,而不是set_age()")
Student.set_age=set_age s2=Student()
s2.set_age(45)
print("s2.age:",s2.age)

运行结果:

小结

通常情况下,上面的set_score方法可以直接定义在class中,但动态绑定允许在程序运行的过程中动态给class加上功能,这在静态语言中很难实现。

使用_ _slots_ _

(1)__slots__是一个特殊变量,而不是一种方法

(2)为了达到限制实例的属性这一目的,在定义class时,定义一个_ _slots_ _特殊变量,来限制该class实例添加的属性:

注意:1、_ _slots_ _仅对当前类实例起作用,对继承的子类是不起作用的

2、子类中的属性=其子类本身的属性∪继承的父类中的属性,取并集

3、使用_ _slots_ _添加属性时,一定要给属性加上引号,因为用tuple绑定的是属性名称

4、给__slots__添加属性时,使用的是元组tuple

 class Student(object):
__slots__=('name','age') #用tuple定义允许绑定的属性名称 s=Student() #创建一个实例
s.name='Maria'
s.age=23
# s.score=99 由于在类Student中限制了属性,所以添加score就会报错
print("s.name:",s.name)
print("s.age:",s.age) print("给s实例添加score属性后,报错类型为:")
try:
s.score=99
except AttributeError as e:
print("AttributeError:",e) print(' ')
print("下面定义了子类:")
class Graduate(Student): #继承了Student类,那么也继承了Student类的_ _slots_ _
__slots__=('score') #这在子类中也定义了_ _slots_ _
#看出下面g.name和g.age都正常,说明子类继承了Student类的_ _slots_ _
g=Graduate()
g.score=99
g.name='shirley'
g.age=34
print("g.name,g.age:",g.name,g.age)
print("g.score:",g.score)

运行结果:


使用__slots__变量后,子类中属性:取并集

父类中使用了__slots__变量:

a. 子类中没有使用该变量,取并集之后,子类中的属性仍然为所有属性

b.  子类中使用了__slots__变量,取并集之后,子类中的属性为子类中的属性+父类中的属性

注:如果父类中的属性定义了私有变量,子类中使用这个属性时,要注意变形

这个结论可以从上面的代码运行查看结果


@property

装饰器(decorator):

(1)可以给函数动态加上功能

(2)对于类的方法,@property 装饰器负责把一个方法变成属性调用

在类的方法中,如何使用@property?

把一个getter方法变成属性,只需要加上@property就可以了,此时,@property 本身又创建了另一个装饰器 @score.setter,负责把一个setter方法变成属性赋值。

只读属性:只定义getter方法,不定义setter方法的属性

可读写属性:既定义getter方法,又定义setter方法的属性

 class Student(object):

     @property
def score(self): #为什么要用score,因为score是属性名
return self._score @score.setter
def score(self,value): #为什么要用score,因为score是属性名
if not isinstance(value,int):
raise ValueError("score must be an integer!")
if value<0 or value>100:
raise ValueError("score must between 0 ~ 100!")
self._score=value s=Student()
s.score=78 #OK,实际转化为s.set_score(60)
print("s.score:",s.score) #OK,实际转化为s.get_score()

运行结果:

多重继承

通过多重继承,一个子类就可以同时获得多个父类的所有功能

参考:https://kevinguo.me/2018/01/19/python-topological-sorting/

多重:多个, 多重继承:即同时继承多个对象

MixIn

(1)MixIn的目的就是给一个类增加多个功能。    解决对同一对象用不同标准来分类的问题。

举例:从性别划分,你是男人;从国家划分,你是中国人;从职业划分,你是程序员; mixin可以轻松定义具备跟你一样特征的人:中国男程序员(天朝屌丝逗比程序员)

(2)定义

在设计类的继承关系时,通常,主线都是单一继承下来的,但是,如果需要“混入”额外的功能,通过多重继承就  可以实现,这种设计通常称之为MixIn.

(3)小结

python允许使用多重继承,因此,MixIn就是一种常见的设计

只允许单一继承的语言(如JAVA),不能使用MixIn设计

定制类

__str__

将__str__方法理解成,规范化输出格式,输出用户能看懂的格式,在Python编辑器中调用print语句返回结果时使用

__repr__:在Python编辑器中直接输入调用变量返回结果时使用

直接显示变量调用的不是__str__( ),而是__repr__( ),两者的区别:

__str__( )返回用户看到的字符串

__repr__( )返回程序开发者看到的字符串,__repr__( )是为调试服务的

通常情况下,__repr__( )和 __str__( )的代码相同,所以偷懒时可以写成这种形式:__repr__=__str__ ,类似赋值,把__str__ 里的代码赋给__repr__

 class Student(object):
def __init__(self,name):
self.name=name s = Student("Michael")
print(s)

运行结果:

下面使用了__str__方法后:

 class Student(object):
def __init__(self,name):
self.name=name
def __str__(self):
return 'Student Object (name :%s) ' % self.name #返回的内容,自己可以修改 s=Student("Michael")
print(s)

返回用户能够看懂的格式:

__str__方法 return 后面的语句可以自己修改

 class Student(object):
def __init__(self,name):
self.name=name
def __str__(self):
return "Hello, My name is %s" % self.name s=Student("Michael")
print(s)

__iter__

如果一个类想被用于for ……in 循环,类似list、tuple那样,就必须使用一个__iter__( )方法, 该方法返回一个迭代对象

举例:以斐波那契数列为例,写一个Fib类,可以作用于for循环:

 class fib(object):
def __init__(self):
self.a,self.b=0,1 def __iter__(self):
return self #实例本身就是迭代对象,所以返回自己 def __next__(self):
self.a,self.b=self.b,self.a+self.b
if self.a>500:
raise StopIteration()
return self.a for n in fib():
print(n)

__getitem__

Fib实例虽然能作用于for 循环,看起来和list有点像,但是,把它当成list来使用还是不行。比如,根据下标取对应位置的元素,需要实现__getiem__( )方法

 class fib(object):
def __init__(self):
self.a,self.b=0,1 def __iter__(self): #__iter__方法,返回一个迭代对象
return self def __next__(self):
self.a,self.b=self.b,self.a+self.b
if self.a>100:
raise StopIteration
return self.a def __getitem__(self,n): #__getitem__方法
a,b=1,1
for x in range(n):
a,b = b, a+b
return a f=fib()
print('f[0]:',f[0])
print("f[1]:",f[1])
print("f[2]:",f[2])
print("f[3]:",f[3])
print("f[13]:",f[13])

运行结果:

__getattr__

正常情况下,当调用的类的方法或属性不存在时,就会报错。要避免这个错误,Python有一个机制,就是写一个__getattr__( )方法,动态返回一个属性。

注意:只有在没有找到属性的情况下,才调用__getattr__,已有的属性,不会在__getattr__中查找。

 class Student(object):
def __init__(self,name):
self.name=name # 注:如果有多个属性查找不存在时,写在一起,而不是再重新写一个__getattr__方法
def __getattr__(self, attr):
if attr=='score':
return 78
if attr=='age':
return lambda :23 #因为lambda是一个函数,所以调用是要加上括号
raise AttributeError('\'Student\' object has no attrbuite :%s'%attr) s=Student("kitty")
print(s.name)
print(s.score)
print(s.age())
print(s.birth)

运行结果:

__call__

调用实例方法,方法 一:使用“实例.方法( )”,”“instance.method( )”形式来调用,如下代码:

 class Student(object):

     def get_score(self):
return self._score def set_score(self,value):
if not isinstance(value,int):
raise ValueError("score must be an integer!")
if value<0 or value>100:
raise ValueError("score must between 0 ~ 100!")
self._score=value s=Student()
s.set_score(78)
#s是实例,调用实例的方法,格式:实例.方法(),instance.method()
print("s.score:",s.get_score())

调用实例的方法,方法二: 直接在实例本身上调用

如何实现:任何类,只需要定义一个__call( )方法,就可以直接对实例进行调用

(我的理解好像只有调用实例的call方法时,才可以直接对实例进行调用,而调用其他的方法时,

仍需要采用 " 实例.方法( )"这种格式。另外,下面代码中定义了__call__ 方法,直接在实例本身上调用时,也没看出什么特别的)

 class Student(object):
def __init__(self, name,score):
self.name = name
self.score=score def __call__(self):
print('My name is %s.' % self.name)
print('My score is %s.' % self.score)
return "" #如果不加这一句,那么返回的结果会有None s=Student('Michael',89)
print(s())
print(s.name)

判断一个对象是否能被调用,能被调用的对象就是一个Callable对象。callable( )函数

函数和带有__call__( )类的实例的实例都是Callable对象

callable()函数,可以判断一个对象是否是“可调用”对象

 class Student(object):
pass s=Student()
print("callable(s):",callable(s))
print("callable(max):",callable(max))
print("callable([1,2,3]):",callable([1,2,3]))
print("callable('str'):",callable('str'))
print("callable(None):",callable(None))

枚举类

from enum import Enum

enum:是一个模块,即一个Python文件,

Enum:是一个类名  , Enum( )相当于创建一个实例

Value属性则是自动赋给成员的int常量,默认从1开始计数

小结:Enum可以把一组相关常量定义在一个class钟,且class不可变,而且成员可以直接比较。

 from enum import Enum

 Month=Enum('hello',('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'))

 for name, member in Month.__members__.items():
print(name,'==>',member,'==>',member.value) print(Month.Jan)
#print(hello.Jan)#会报错

运行结果:

实例:

 #从Enum派生出自定义类
from enum import Enum,unique @unique #@unique装饰器可以检查保证没有重复值
class weekday(Enum):
Sun=0
Mon=1
Tue=2
Wed=3
Thu=4
Fri=5
Sat=6 print('weekday.Mon:',weekday.Mon)
print("weekday['Mon']:",weekday['Mon'])
#注意:因为中括号[]里有了单引号,所以外面要使用双引号括起来
print("weekday(1):",weekday(1))
print("weekday.Mon.value:",weekday.Mon.value)

运行结果:

实战:把Student类中的gender属性改成枚举类型,可以避免使用字符串:

 from enum import   Enum, unique
@unique
class Gender(Enum):
Male = 0
Female = 1 class Student(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender bart = Student('Bart', Gender.Male)#创建一个实例,性别采用Gender类中的格式
if bart.gender == Gender.Male:
print(bart.gender)
print('测试通过!')
else:
print('测试失败!')

运行结果:

元类

type( )

动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运行时动态创建的。

作用:(1)可以查看一个类型或变量的类型

(2)创建出新的类

创建class对象时,type( )函数传入的3个参数:

a. 类名

b. 继承的父类集合,注意Python支持多重继承,如果只有一个父类,记住元组tuple的单元素写法,要加上逗号

c. 类的方法名和函数绑定

个人理解:即使我们使用 class Xxx……这种格式来定义类,Python解释器遇到class定义时,仅仅是扫描一下class定义的语法,仍会使用type ( )函数来创建类class.

metaclass 元类

根据metaclass创建出类,即是:先定义metaclass,然后创建类。

连接起来就是:先定义metaclass,就可以创建类,最后创建实例。

metaclass允许创建类或者修改类

Python面向对象 -- slots, @property、多重继承MixIn、定制类(str, iter, getitem, getattr, call, callable函数,可调用对象)、元类(type, metaclass)的更多相关文章

  1. 线程的函数中调用MFC对话框类的变量

    线程的函数中调用MFC对话框类的变量多线程传输文件的对话框 现在想要在对话框上添加一个进度条 为进度条映射变量m_progress这就需要在传输一段文件后就更新m_progress的值使进度条前进 也 ...

  2. Python面向对象高级编程-__slots__、定制类,枚举

    当在类体内定义好各种属性后,外部是可以随便添加属性的,Python中类如何限制实例的属性? Python自带了很多定制类,诸如__slots__,__str__ __slots__ __slots__ ...

  3. python基础语法20 面向对象5 exec内置函数的补充,元类,属性查找顺序

    exec内置函数的补充 exec: 是一个python内置函数,可以将字符串的代码添加到名称空间中; - 全局名称空间 - 局部名称空间 exec(字符串形式的代码, 全局名称空间, 局部名称空间) ...

  4. Python学习 Day 9 property 多重继承 Mixin

    在绑定属性时,如果我们直接把属性暴露出去,虽然写起来很简单,但是,没办法检查参数,导致可以把成绩随便改: s = Student() s.score = 9999 为了限制score的范围,可以通过一 ...

  5. Python - 面向对象编程 - @property

    前言 前面讲到实例属性的时候,我们可以通过 实例对象.实例属性 来访问对应的实例属性 但这种做法是不建议的,因为它破坏了类的封装原则 正常情况下,实例属性应该是隐藏的,只允许通过类提供的方法来间接实现 ...

  6. python 面向对象七 property() 函数和@property 装饰符

    一.property引入 为了使对象的属性不暴露给调用者和进行属性值检查,设置了访问属性的接口函数,使用函数访问属性,并可以在函数内部检查属性. >>> class Student( ...

  7. java 面向对象(四十二):反射(六)反射应用三:调用运行时类的指定结构

    调用指定的属性: @Test public void testField1() throws Exception { Class clazz = Person.class; //创建运行时类的对象 P ...

  8. C++11 function用法 可调用对象模板类

    std::function<datatype()> ()内写参数类型 datatype 代表function的返回值 灵活的用法.. 代码如下 #include <stdio.h&g ...

  9. Python元类之由浅入深

    前言 ​ 元类属于python面向对象编程的深层次的魔法,非常重要,它使我们可以更好的掌控类从创建到消亡的整个生命周期过程.很多框架的源码中都使用到了元类.例如 Django Framework 中的 ...

随机推荐

  1. ZYNQ笔记(0):C语言基础知识复习

    ZYNQ的SDK是用C语言进行开发的,C语言可以说是当今理工类大学生的必备技能.我本科学C语言时就是对付考试而已,导致现在学ZYNQ是一脸懵逼.现在特开一帖,整理一下C语言的基础知识. 一.定义 1. ...

  2. 配置linux命令行界面的 文件显示颜色

    在linux命令行界面下使用ls命令时,有时会看见显示的文件会有不同的颜色,因为linux的文件没有后缀名这个概念(Windows系统中的文件会有后缀名,从而可以将文件标识为不同类型),显示不同的颜色 ...

  3. P1361 小M的作物 (最大流)

    题目 P1361 小M的作物 解析 把\(A\)看做源点,把\(B\)看做汇点,先不考虑额外情况 显然,这是一种两者选其一的问题,我们选择一部分边割去,使这部分边的贡献最小,就是求最小割,我们求出了收 ...

  4. Vue学习之webpack调用第三方loader(十五)

    ---恢复内容开始--- 一.webpack 默认只能打包处理 JS 类型的文件,无法处理 其他的非  JS 类型的文件: 如果非要处理 非 JS 类型的文件,我们需要手动安装一些 合适 第三方 lo ...

  5. 数据分析 之 NumPy

    目录 简单了解数据分析 Python数据分析三剑客(Numpy,Pandas,Matplotlib) 简单使用np.array() 使用np的routines函数创建数组 ndarray N维数组对象 ...

  6. 归并排序python实现源码

    将开发过程经常用到的一些代码片段收藏起来,下面的资料是关于归并排序python实现的代码,应该能对码农们有一些用. def mergesort(arr): if len(arr) == 1: retu ...

  7. .net 获取CPU频率 内存 磁盘大小,域名 端口 虚拟目录等

    CPU个数: @Environment.GetEnvironmentVariable("NUMBER_OF_PROCESSORS") CPU类型: @Environment.Get ...

  8. Windows下搭建TensorFlow的GPU版本

    1.下载python3.5.2版本并安装(必须是3.5版本,而且3.5后不带字母的版本) 2.使用下面的地址下载tensorflow的GPU版本 http://www.lfd.uci.edu/~goh ...

  9. MySQL AutoIncrement--自增锁模式

    自增锁模式 在MYSQL 5.1.22版本前,自增列使用AUTO_INC Locking方式来实现,即采用一种特殊的表锁机制来保证并发插入下自增操作依然是串行操作,为提高插入效率,该锁会在插入语句完成 ...

  10. 基于centos7.6离线部署开k3s

    K3S简介: https://k3s.io/ https://github.com/rancher/k3s https://github.com/rancher/k3s/releases      / ...