面向对象
类定义
类是独立存放变量(属性、方法)的空间
类的定义用关键字 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):
print("C")
如果继承的父类里面有相同的方法,则选择A,即最前边的类
class Base(object):
def play(self):
print("Base")
class A(Base):
def play(self):
print("A")
class B(Base):
def play(self):
print("B")
class C(A, B):
print("C")
j = C()
j.play()
但是如果必须用B呢?
这时可以重写B类的play
方法
class Base(object):
def play(self):
print("Base")
class A(Base):
def play(self):
print("A")
class B(Base):
def play(self):
print("B")
class C(A, B):
# 重写PLAY方法
def learn(self):
print("B")
j = C()
j.play() # A类方法play
j.learn() # B类方法play,重写之后别名learn
添加一个
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