面向对象

类定义

类是独立存放变量(属性、方法)的空间

类的定义用关键字 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就是子类。

换个方式理解

img

  • 这里的生物就是父类,植物和动物就是子类,他们都有相同的属性,但是子类又有自己特有的属性,要增加这种属性的过程,就叫做方法重写
  • 很多时候需要用到变量,在子类中使用变量的时候,他首先会去子类本身寻找属性,如果不存在,就去父类里面寻找
  • 更多的时候,在类的外边和里边都有变量,而且相同,他们不会相互影响,这就是变量空间,而变量的交互,只能通过传参进去。类似于函数的局部和全局变量,但是他们最根本的区别就是类生成的实例,也是有自己的变量空间。

重写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 的时候,发现其实变成了同一个实例

当不想有很多的对象,而且每次生成的实例只有一个,这时就可以用单例

从始至终,只生成一个实例

定制访问函数

内容非常简单,如图

img

img

当访问的属性不存在时,希望出现错误信息而不报错,就可以用 __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内置了很多类装饰器

常用的内置类装饰器

img

如果需要用类作装饰器,就需要定义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
posted @ 2019-01-06 17:06  ChanceySolo  阅读(255)  评论(0编辑  收藏  举报