Python基础之(面向对象进阶)

一、面向对象结构与成员

class A:
    name = 'jump'  # 静态变量(静态字段)
    __id = '110'  # 私有静态变量(私有静态字段)

    def __init__(self,name,age): #   初始方法  

        self.name = name  #对象属性(普通字段)
        self.__age = age  # 私有对象属性(私有普通字段)

    def func(self):  # 普通方法(实例方法)
        pass

    def __func(self): #私有方法
        print(666)

    @classmethod  # 类方法
    def class_func(cls):
        """ 定义类方法,至少有一个cls参数 """
        print('类方法')

    @staticmethod  #静态方法
    def static_func():
        """ 定义静态方法 ,无默认参数"""
        print('静态方法')

    @property  # 属性
    def prop(self):
        pass

1.1、字段

class Run:
    name = "jump"
    __id = 110
    def __init__(self,a,b,c):
        self.a = a
        self.__b = b
        self._c = c
    def run(self):
        return (self.a*self.__b*self._c)
    def __ss(self):
        print("xxx")
    def ss(self):
        self.__ss()
class Jump(Run):
    pass

print(Run.name)     #类直接访问静态字段
print(Jump.name)   #类继承访问静态字段

jump = Jump(2,3,4)
print(jump.a,jump.name)   #继承后对象访问普通字段及静态字段
r = Run(1,2,3)
print(r.name,r.a)                       # 对象访问普通字段及静态字段

print(r.run())        #访问私有字段
print(r._c)
print(r._Run__id)
print(r._Run__b)  #直接访问私有字段
r._Run__ss()      #直接访问私有方法
r.ss()  #间接调用

执行结果:

Python中并无真正的私有,只有约定的私有或者叫隐藏,在上一节末尾已经说过

1.2、方法包括:普通方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同。

  • 普通方法:至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self;
  • 类方法: 至少一个cls参数;执行类方法时,自动将调用该方法的类复制给cls;
  • 静态方法:无默认参数;(类的工具包)
class Jump:
    @classmethod
    def head(cls,a):
        print("类方法",a)
    @staticmethod
    def legs(*args,**kwargs):
        print("静态方法",*args)
    def body(self,c):
        print("普通方法",c)
Jump.head("a")  #调用类方法
Jump.legs("b")   #调用静态方法

jump = Jump()  
jump.body("c")  #调用普通方法
jump.legs("b")   #调用静态方法
jump.head("a")  #调用类方法

执行结果:

1.3、属性

property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值。为什么要用property?将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则

class Jump:
    @property
    def body(self,):
        print("aaa--")
        return "xxx"
jump = Jump()
ret=jump.body
print(ret)  #返回结果xxx

由于新式类中具有三种访问方式,我们可以根据他们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除

class Jump:
    @property
    def AAA(self):
        print('get的时候')
    @AAA.setter
    def AAA(self,value):
        print('set的时候')
    @AAA.deleter
    def AAA(self):
        print('delete的时候')
#只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter
f1=Jump()
f1.AAA
f1.AAA='aaa'
del f1.AAA

或者:
class Jump:
    def get_AAA(self):
        print('get的时候')
    def set_AAA(self,value):
        print('set的时候')
    def delete_AAA(self):
        print('delete的时候')
    AAA=property(get_AAA,set_AAA,delete_AAA) 
    #内置property三个参数与get,set,delete一一对应

f1=Jump()
f1.AAA
f1.AAA='aaa'
del f1.AAA

  

二、isinstance(obj,cls)和issubclass(sub,super)

isinstance(obj,cls)检查obj是否是类 cls 的对象

class A:pass
class B(A):pass
a=B()
print(isinstance(a,A))   #True
print(isinstance(a,B))  #True

issubclass(sub, super)检查sub类是否是 super 类的子类

class A:pass
class B(A):pass
print(issubclass(B,A))  #True

 

三、反射概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。

python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中四个可以实现自省的函数:

#hasattr(object,name)
#判断name是否在object,判断object中是否有一个叫name的方法或者属性
#getattr(object, name, default=None)
取object中name 的值,default为找不到报错信息,否则找不到报错
#setattr(x, y, v)
给x对象中的y属性设置值v
#delattr(x, y)
#删除对象x中y  (无法删除类方法,报错找不到)

class A:
    def __init__(self,name):
        self.name = name
    def body(self):
        print("body")
    def head(self):
        print("head")

a = A("jjj")
print(hasattr(a,"arm"))
ret=getattr(a,"body","xx")
ret()
print(a.__dict__)

re = getattr(a,"hegad","报错")
print(re)

def bbbb(c):
    return (c.name)
setattr(a,"junm","xxx")
setattr(a,"aa",bbbb(a))  #增加一段代码运行结果
setattr(a,"dd",lambda self:self.name)  #增加一个匿名函数
print(a.__dict__)
delattr(a,"junm")
print(a.__dict__)

 运行结果:

 

四、其他特殊成员

4.1、__setattr__,__delattr__,__getattr__

#__getattr__(self, item)
#属性不存在时候触发 (可用于报错或传递,配合getattr)
__setattr__(self, key, value)
#设置属性的时候触发
__delattr__(self, item)
#删除属性的时候触发

class Abc:
    def __init__(self,a,b):
        self.a = a
        self.b = b
    def __getattr__(self, item):
        print("get",item)
    def __setattr__(self, key, value):
        print("abc",key,value)
        #self.__dict__[key]=value #应该使用它
    def __delattr__(self, item):
        print("del",item)
    self.__dict__.pop(item)

abc = Abc("a","b")
print(abc.__dict__)   #重写了setattr,并没写入,所以为空
print(abc.xx)
del abc.b
print(abc.__dict__)

执行结果:

补充:__getattribute__

属性有或者没有都会触发,(抛出异常attributeError,后执行__getattr__)

4.2、利用(__getattr__和继承)重写数据类型的方法

#继承方式
class List(list):
    def __init__(self,li,tag=False):
        super().__init__(li)
        self.tag = tag
    def append(self,obj):  #重写append
        if type(obj) is str:
            super().append(obj)
        else:
            print("xxx")
    def clear(self):    #重写clear
        if self.tag is True:
            super().clear()
        else:
            print("no")
l = List([1,2,3])
print(l)
l.append(123)
print(l)
l.append("123")
l.clear()
print(l)

l = List([4,5,6],tag=True)
l.clear()
print(l)

 执行结果:

#getattr与__getattr__
import time
class Openfile:
    def __init__(self,filename,mode="w",encoding='utf-8'):
        self.openfile = open(filename,mode=mode,encoding=encoding)
    def write(self,ll):
        t = time.strftime('%Y-%m-%d %X')
        self.openfile.write(t+ll)
    def __getattr__(self, item):
        return getattr(self.openfile,item)

f = Openfile("1.txt","w+")
f.write("cpu报警")
f.seek(0)
print(f.read())

#2018-12-28 16:27:24cpu报警

4.3、__setitem__,__getitem,__delitem__

class Abc:
    def __init__(self,name):
        self.name=name

    def __getitem__(self, item):
        print(self.__dict__[item])
        print("get")
    def __setitem__(self, key, value):
        self.__dict__[key]=value
        print("set")
    def __delitem__(self, key):
        print('del')
        self.__dict__.pop(key)

abc=Abc('sb')
abc['age']=18  #执行__setitem__
abc["age"]        #执行__getitem__
del abc['age']    #执行__delitem__

#对象字典方式触发

4.4、__str__,__repr__,__doc__

class A:
    "Is me"
    def __str__(self):
        return "jump"
    def __repr__(self):
        return "crazy"
a=A()
print(a)  #返回jump,当__str__被注销就返回crazy
#打印对象时,默认输出该方法的返回值

print(A.__doc__)  #Is me

4.5、__module__与__class__

class A:
    pass
obj = A()
print(obj.__module__)  #实例来自哪个模块
print(obj.__class__)   #实例来自哪个类 

4.6、__next__和__iter__实现迭代器协议(在迭代器的时候已经说过)

from collections import Iterable
from collections import Iterator
class a():
    def __next__(self):pass
    def __iter__(self):pass   #为a定义两个方法
dd=a() #实例化
print(isinstance(dd, Iterator))
print(isinstance(dd, Iterable))
#结果都为True,如果去掉__next__方法即第一个结果为False
#迭代器对象一定是可迭代对象,而可迭代对象不一定是迭代器对象

4.7、__call__

class A:
    def __init__(self):
        pass
    def __call__(self, *args, **kwargs):
        print("xxx")
a= A()   #执行__init__
a()      #执行__call__
# __call__ 方法的执行是由对象后加括号触发的

4.8、__del__

析构方法,当对象在内存中被释放时,自动触发执行

注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。

4.9、描述符(__get__,__set__,__delete__)

描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议,描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)

描述符分两种:

  • 数据描述符:至少实现了__get__()和__set__()方法
  • 非数据描述符:没有实现__set__()方法

__get__():调用一个属性时,触发
__set__():为一个属性赋值时,触发
__delete__():采用del删除属性时,触发

#定义一个描述符
class Abc: #在python3中Abc是新式类,它实现了三种方法,这个类就被称作一个描述符
    def __get__(self, instance, owner):
        pass
    def __set__(self, instance, value):
        pass
    def __delete__(self, instance):
        pass
#怎么使用描述符
class Abc:
    def __get__(self, instance, owner):
        print("get" ,instance,owner)
    def __set__(self, instance, value):
        print("set",instance,value)
    def __delete__(self, instance):
        print("delete",instance)
class A:
    num = Abc()
    def __init__(self,num):
        self.num = num
        
a=A("abc")
a.num
del a.num

#set <__main__.A object at 0x00A525F0> abc
#get <__main__.A object at 0x00A525F0> <class '__main__.A'>
#delete <__main__.A object at 0x00A525F0>

来看个描述符具体应用的例子:(定义传入数据的类型)

class Abc:
    def __init__(self,name,type):
        self.name = name
        self.type = type
    def __get__(self, instance, owner):
        print("get" ,instance,owner)
        return instance.__dict__[self.name]
    def __set__(self, instance, value):
        if not isinstance(value,self.type):
            print("%s 不是 %s" %(self.name,self.type))
        else:
            instance.__dict__[self.name] = value
class A:
    name = Abc("name",str)
    age = Abc("age",int)
    job = Abc("job",str)
    def __init__(self,name,age,job):
        self.name = name
        self.age = age
        self.job = job
a=A("abc",5,2)
print(a.name)
print(a.__dict__)

#job 不是 <class 'str'>
#get <__main__.A object at 0x00682810> <class '__main__.A'>
#abc
#{'name': 'abc', 'age': 5}

ps:描述符注意事项:

  • 描述符本身应该定义成新式类,被代理的类也应该是新式类
  • 必须把描述符定义成这个类的类属性,不能为定义到构造函数中
  • 要严格遵循该优先级,优先级由高到底分别是

     类属性、数据描述符、实例属性、非数据描述符、找不到的属性触发__getattr__()

 

4.10、类的装饰器

#无参数
def demo(cls):
    print("开始装饰类")
    return cls

@demo
class A:
    def __init__(self,name,age):
        self.name = name
        self.age = age

a = A("jump",12)
#开始装饰类

#有参数
def demo(**kwargs):
    def cls(c):
        print("开始装饰",kwargs)
        return c
    return cls

@demo(name=1,age=2)
class A:
    def __init__(self,name,age):
        self.name = name
        self.age = age
a = A("jump",12)
#开始装饰 {'name': 1, 'age': 2}

类的装饰器应用:(设置类属性)

def demo():
    def cls(c):
        c.x = 1
        c.y = 2
        return c
    return cls
@demo()
class A:
    pass
print(A.__dict__)


#{....,'x': 1, 'y': 2}

 PS:描述符应用:

描述符与类装饰器的应用:

class Abc:
    def __init__(self,name,type):
        self.name = name
        self.type = type
    def __get__(self, instance, owner):
        print("get" ,instance,owner)
        return instance.__dict__[self.name]
    def __set__(self, instance, value):
        if not isinstance(value,self.type):
            print("%s 不是 %s" %(self.name,self.type))
        else:
            instance.__dict__[self.name] = value

def demo(**kwargs):
    def cls(c):
        for k,v in kwargs.items():
            setattr(c,k,Abc(k,v))  #设置属性,触发描述符__set__
        return c
    return cls

@demo(name=str,age=int,job=str)
class A:
    def __init__(self,name,age,job):
        self.name = name
        self.age = age
        self.job = job
a=A("abc",5,2)
print(a.name)
print(a.__dict__)


#job 不是 <class 'str'>
#get <__main__.A object at 0x025F0CF0> <class '__main__.A'>
#abc
#{'name': 'abc', 'age': 5}

 描述符实现@classmethod、@staticmethod、@property

@classmethod
class Abc:
    def __init__(self,fun):
        self.fun = fun
    def __get__(self, instance, owner):
        def aaa(*args,**kwargs):
            print("aaa")
            return self.fun(owner,*args,**kwargs)
        return aaa
class A:
    @Abc
    def a(cls):
        print("我是类")
A.a()

@staticmethod
class Abc:
    def __init__(self,fun):
        self.fun = fun
    def __get__(self, instance, owner):
        def aaa(*args,**kwargs):
            print("aaa")
            return self.fun(*args,**kwargs)
        return aaa
class A:
    @Abc
    def a(*args,**kwargs):
        print("我是静态")
a = A()
a.a()

@property
class Abc:
    def __init__(self,fun):
        self.fun = fun
    def __get__(self, instance, owner):
        return self.fun(instance)
class A:
    @Abc
    def a(self):
        return "123"
print(A.a)

4.11、单类模式

单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案

class A:
    __instance = None
    def __init__(self):
        pass

#一般用于日志文件等,单例模式用来保证内存中仅存在一个实例!!!

 补充: __new__,其实__init__是在类实例被创建之后调用的,它完成的是类实例的初始化操作,而 __new__方法正是创建这个类实例的方法。

class Person:
    def __new__(cls, *args, **kwargs):
        print('调用__new__,创建类实例')
        return super().__new__(Person)

    def __init__(self, name, age):
        print('调用__init__,初始化实例')
        self.name = name
        self.age = age

    def __str__(self):
        return '<Person: {}({})>'.format(self.name, self.age)


p1 = Person('张三', 24)
print(p1)


#结果:
调用__new__,创建类实例
调用__init__,初始化实例
<Person: 张三(24)>

__new__方法在类定义中不是必须写的,如果没定义的话默认会调用object.__new__去创建一个对象(因为创建类的时候默认继承的就是object)。

如果我们在类中定义了__new__方法,就是重写了默认的__new__方法,我们可以借此自定义创建对象的行为。

class Singleton:
    # 重写__new__方法,实现每一次实例化的时候,返回同一个instance对象
    def __new__(cls, *args, **kw):
        if not hasattr(cls, '_instance'):
            cls._instance = super().__new__(Singleton)
        return cls._instance

  

posted @ 2018-12-29 15:40  Crazyjump  阅读(413)  评论(0编辑  收藏  举报