一、类定义
类是独立存放变量(属性、方法)的空间
类的定义用关键字 class 来定义
class 类名(): 属性名 = '属性值' pass
增加属性
>>> class Person(): val = 'chancey' pass >>> Person.val1 = 'Mary' >>> print(Person.val) chancey >>> print(Person.val1) Mary
实例
创建一个实例对象使用 p = Person()
例如:
class Person(): #定义一个类 gender = 'boy' p1 = Person() #实例化 p2 = Person() p1.name = 'Chancey' #增加属性 p2.name = 'Mary' print(p1.name,p1.gender) #调用属性 print(p2.name,p2.gender)
私有属性
私有属性用_开头,强制性私有属性用__开头
class Person(): gender = 'boy' _age = 18 __height = 180 print(Person.gender) print(Person._age) print(Person.__height)
执行可以看出,_age可以访问,但是__height不能访问
二、方法
方法就是写在类里面的函数,可以用 类名.函数名 或者 实例名.属性 直接调用
class Person(): age = 18 def eat(arg): print('传入的参数是:%s'%arg) Person.eat('chancey') p1 = Person() p1.eat()
可以看到,实例中并没有传入参数,但是控制台却打印出了 传入的参数是:chancey 和 传入的参数是:<__main__.Person object at 0x000000000214E3C8>
因为这是实例化的机制关系
现在在类方法传入 self ,在定义了self之后,这个类方法叫做实例方法
class Person(): age = 18 def eat(self): print('%s正在吃饭。。。'%self.name) p1 = Person() #实例 p1.name = 'chancey' #实例属性 p1.eat() #实例方法
其中的 self.name 就是实例属性,到函数外边才定义的实例属性,这样就可以通过实例方法来访问实例属性
- 类中的方法,就是函数
- self代表的是实例本身
- 方法的调用和属性调用一样,通过点操作符调用,传参和函数传参一样
三、类的初始化
类的初始化用 __init__ 函数实现,会在特定的时机被触法执行,__init__会在实例化之后自动被调用以完成初始化
这样可以实现动态的修改或者赋值
在生成多个实例的时候,调用不同的方法实现不同的效果
class Person(object): def __init__(self,name,age): self.name = name self.age = age def eat(self): print('Chancey is eating !') p = Person('Chancey',18) print(p.eat()) print(p.name)
这是类传参的一种方式,__init__就是在生成一个实例之后,自动调用,然后进行初始化
而这里的self参数,代表的是实例本身,而不是类对象
注意:
类的方法就是类里面的函数,但是类方法必须有一个额外的参数,就是self,并且是第一个参数
应用:
定义一个类,然后计算矩形的周长和面积
class Rect(object): def __init__(self,a,b): self.a = a self.b = b def datil(self): print('该矩形的长是:%s,宽是:%s。'%(self.a,self.b)) def c(self): print('周长是:%s'%((self.a + self.b) * 2)) def s(self): print('面积是:%s'%(self.a * self.b)) p = Rect(1,2) p.datil() p.c() p.s()
四、继承
在代码大量重复的时候,应该优先考虑类的继承
假设上边计算长方形的代码,现在需要计算正方形,就可以用到继承
直接在新类的括号里写上需要继承的类名
class Quare(Rect): pass
就像这样,然后,就可以使用 p = Quare(1,2) 生成实例
同样,调用方法使用 p.c 即可实现相同的效果
这样一来,前边的Rect就是父类,而Quare就是子类。
换个方式理解
这里的生物就是父类,植物和动物就是子类,他们都有相同的属性,但是子类又有自己特有的属性,要增加这种属性的过程,就叫做方法重写
很多时候需要用到变量,在子类中使用变量的时候,他首先会去子类本身寻找属性,如果不存在,就去父类里面寻找
更多的时候,在类的外边和里边都有变量,而且相同,他们不会相互影响,这就是变量空间,而变量的交互,只能通过传参进去。类似于函数的局部和全局变量,但是他们最根本的区别就是类生成的实例,也是有自己的变量空间。
重写init
上边的正方形通过一系列的运行就会发现问题,如果长宽不相等的时候,他依旧会运行,现在写一个在生成实例的时候判断长宽是否相等的方法
class Quare(Rect): def __init__(self,a,b): if a == b: Rect.__init__(self,a,b) else: print("长宽不相等!")
这里的代码逻辑非常简单,重写初始化方法,将派生类(派生就是继承)的init方法重新定义
就是判断出a=b就执行父类的初始化方法,不再赘述
如果后边获取面积的话,直接报错,因为else之后只有print方法
五、多继承
多重继承指的是层层继承,而多继承指一个子类同时继承多个父类
继承方法一样,在多个类之间用逗号隔开
先写三个类
class Base(object): #这里的object不必在意,是在python2.x里面用来区别新式类和老式类,而在python3.x里面一样 def play(self): print("Base") class A(object): def play(self): print("A") class B(object): def play(self): print("B")
然后继承上边的A和B class C(A, B):
如果继承的父类里面有相同的方法,则选择A,即最前边的类
但是如果必须用B呢?
这时可以重写B类的play方法,重写步骤不再赘述
现在将B类修改一下,添加一个 learn(self): 方法,届时,在调用A类的play的同时,还可调用B类的learn
基于多继承的Mix-in设计模式
Min-in设计就是拼积木设计
例如,一般的分类思维是把人分为男人和女人,而现在可以将其分为眼睛、耳朵、鼻子,达到了最高类,而眼睛、耳朵、鼻子又可以拼成人,这就是Mix-in
一般来说,Mix-in是多继承的终点
super
在方法重写之后,还需要用到之前的方法的时候,使用 super().__init__(self) 再次调用即可,原理简单,无需赘述
六、魔术方法
在类里面所有以__包围的类方法,都称之为魔术方法
这里依旧举例说明,重新来定义一个计算面积的类
class Rect(object): def __init__(self,a,b): self.a = a self.b = b def get_ares(self): return self.a * self.b
__add__
添加一个类方法
def __add__(self, other): aa = self.a + other.a bb = self.b + other.b return aa,bb
这是将相同的属性加在一起,并不是叠加
常见的运算符方法
- __add__(self,other) # x+y
- __sub__(self,other) # x-y
- __mul__(self,other) # x*y
- __mod__(self,other) # x%y
- __iadd__(self,other) # x+=y
- __isub__(self,other) # x-=y
- __radd__(self,other) # y+x
- __rsub__(self,other) # y-x
- __imul__(self,other) # x*=y
- __imod__(self,other) # x%=y
__str__
对应到交互式环境,其实是调用的__str__方法,将一个对象转换成字符串
__repr__
对应到交互式环境,就是 return
在python中,str和repr方法在处理对象的时候,分别调用的是对象的__str__和__repr__方法
print也是如此,调用str函数来处理输出的对象,如果对象没有定义__str__方法,则调用repr处理
在 shell 模式下,展示对象 __repr__ 的返回值
__call__
正常情况下,实例是不能像函数一样被调用的,要想实例能够被调用,就需要定义 __call__ 方法
def __call__(self, *args, **kwargs): return 'I am __call__'
届时,实例也可以被调用
__new__
在创建实例对象的时候,首先调用new方法来开辟一条内存空间,然后用init方法创建实例。
其他的魔术方法:
__class__ 查看类名
__base__ 查看继承的父类
__bases__ 查看继承的全部父类
__dict__ 查看全部属性,返回属性和属性值键值对形式
__doc__ 查看对象文档,即类中的注释(用引号注视的部分)
__dir__ 查看全部属性和方法
七、单例
class A(object): def __new__(cls, *args, **kwargs): if not hasattr(cls,'instance'): cls.instance = super.__new__(cls) return cls.instance def __init__(self): self.name = 'Chancey'
生成两个实例 p1=A() 和 p2=A() ,调用属性name 的时候,发现其实变成了同一个实例
当不想有很多的对象,而且每次生成的实例只有一个,这时就可以用单例
从始至终,只生成一个实例
八、定制访问函数
内容非常简单,如图
当访问的属性不存在时,希望出现错误信息而不报错,就可以用 getattr 方法
def __getattr__(self, item): return '内容跑丢了'
九、装饰器
如果有一段代码不可修改,但是要新增功能,这时就需要用到装饰器
现有如下代码
def myfunc(): print('I am eating !')
现在需要修改,增加输出 It's cool 1 ,并且不能更改定义函数时的代码,这时就可以写
def myfunc(): return 'I am eating !' def boo(): return myfunc(),"It's cool !" print(boo())
现在又有新的要求,经理既不让修改定义时的代码又希望使用 print(myfunc()) 调用就能完成功能添加,就可以这样写
def myfunc(): return 'I am eating !' def boo(func): return func(),"It's cool !" myfunc = boo(myfunc) #将函数作为参数传递进去 print(myfunc)
现在发现myfunc本来是一个函数,这样 print(myfunc) 的话,他就是一个字符串,并没有达到要求,再做修改
def myfunc(): return 'I am eating !' def boo(func): def new_func(): #新增一个过度性的函数 return func(),"It's cool !" return new_func #返回该函数 myfunc = boo(myfunc) print(myfunc()) #这时,是调用函数,而不是打印字符串
如果要传参,就将其一层一层的传进来
def myfunc(name): return 'I and %s are eating !'%(name) def boo(func): def new_func(name): return func(name) + " It's cool !" return new_func myfunc = boo(myfunc) print(myfunc('Chancey'))
OK,这就是原生的装饰器,有人称这种方法为“狸猫换太子”
将两个函数换下位置
def boo(func): #这个函数就叫装饰函数 def new_func(name): return func(name) + " It's cool !" return new_func def myfunc(name): return 'I and %s are eating !'%(name) myfunc = boo(myfunc) print(myfunc('Chancey'))
新增的函数就叫做装饰函数,既然python的铭牌是“人生苦短,我用python!”,仔细读 myfunc = boo(myfunc) 实在难理解,像这种繁琐的代码是不会出现在python里面的,所以可以这样写
def boo(func): def new_func(name): return func(name) + " It's cool !" return new_func @boo def myfunc(name): return 'I and %s are eating !'%(name) print(myfunc('Chancey'))
@语法叫语法糖,用来简化代码,使代码更加具有可读性,以上的 @boo = myfunc = boo(myfunc)
类装饰器也是如此,python内置了很多类装饰器
常用的内置类装饰器
如果需要用类作装饰器,就需要定义call方法
十、描述符
在一个类里面实例化另外一个类的时候需要用到描述符,如果对这个实例访问的时候,必须定义 __get__ 、 __set__ 和 __delete__
如果调用了get和set,就是一个数据描述符,如果只调用了get,则是非数据描述符
class MyClass(): def __get__(self, instance, owner): print("设置成功") def __set__(self, instance, value): print('******%s*****'%value) def __delete__(self, instance): print('释放成功') class Att(): p = MyClass() q = Att() print(q) q.Att = 10