python基础_多态+封装+反射

多态:

指一个事物有多种状态。

import abc
class Animal(metaclass=abc.ABCMeta): #同一类事物:动物
    @abc.abstractmethod
    def talk(self):
        pass

class People(Animal): #动物的形态之一:人
    def talk(self):
        print('say hello')

class Dog(Animal): #动物的形态之二:狗
    def talk(self):
        print('say wangwang')

class Pig(Animal): #动物的形态之三:猪
    def talk(self):
        print('say aoao')

多态性:

指不同的实例对象调用同一个函数,会产生行为。

多态性的好处:无需修改调用方式,使用者都是用一种调用方式。

class Cat(Animal): #属于动物的另外一种形态:猫
    def talk(self):
        print('say miao')

def func(animal): #对于使用者来说,自己的代码根本无需改动
    animal.talk()

cat1=Cat() #实例出一只猫
func(cat1) #甚至连调用方式也无需改变,就能调用猫的talk功能

say miao

隐藏:

一般单下划线,和双下划线都是仅供内部调用的数据或函数。

在python中用双下划线开头的方式将属性隐藏起来(设置成私有的)

#其实这仅仅这是一种变形操作且仅仅只在类定义阶段发生变形
#类中所有双下划线开头的名称如__x都会在类定义时自动变形成:_类名__x的形式:

class A:
    __N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N
    def __init__(self):
        self.__X=10 #变形为self._A__X
    def __foo(self): #变形为_A__foo
        print('from A')
    def bar(self):
        self.__foo() #只有在类内部才可以通过__foo的形式访问到.

#A._A__N是可以访问到的,
#这种,在外部是无法通过__x这个名字访问到。

在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的

封装:

封装只是为了区分内外,并不是不让使用,封装的属性在内部可以直接使用,在外部不能直接使用,然而定义属性的目的终归是要用的,这个时候就要开辟一个接口。

封装数据:将数据隐藏起来,开辟一个操作该数据的接口,然后在接口上附加上限制操作,完成对数据属性的限制。

封装方法:隔离复杂度,使用者只需懂接口就好了,无需知道内部实现。

python并不会真的阻止你访问私有的属性,模块也遵循这种约定,如果模块名以单下划线开头,那么from module import *时不能被导入,但是你from module import _private_module依然是可以导入的。

property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值。

静态属性:与实例绑定,默认传入self,也是函数属性,调用不用加括号,可以隐藏其逻辑,像调用数据属性一样。

类方法:不与实例绑定,与类绑定,默认传入class,类和实例都可调用,主要用于类需要直接调用的方法。

静态方法:不与实例绑定,不需要传入self,class。类与实例都可调用。----------类的工具包,未传入self,cls,所以这个函数内,不能调用类和实例的属性。

class Room:
    tag = 1

    def __init__(self, name, owner, width, length, heigh):
        self.name = name
        self.owner = owner
        self.width = width
        self.length = length
        self.heigh = heigh

    @property
    def cal_area(self):
        return self.width * self.length

    def test(self):
        print('from test', self.name)

    @classmethod
    def tell_info(cls, x):
        print(cls)
        print('--》', cls.tag, x)

    @staticmethod
    def wash_body(a, b, c):
        print('%s %s %s正在洗澡' % (a, b, c))


r1 = Room('厕所', 'alex', 100, 100, 100000)
print(r1.cal_area)
Room.tell_info(10)
r1.wash_body('alex1','alex2','alex3')
Room.wash_body('alex1','alex2','alex3')

10000
<class '__main__.Room'>
--》 1 10
alex1 alex2 alex3正在洗澡
alex1 alex2 alex3正在洗澡

反射:

通过字符串的形式操作对象的相关属性。python中的一切都是对象,都可以使用反射。

反射主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。

四个可以实现自行的函数:

hasattr(object,name)
判断object中有没有一个name字符串对应的方法或属性
getattr(object,name,default=None)
getattr(x, 'y')就等于x.y.
默认值,如果访问的属性存在,就和没有默认值一样,若访问的属性不存在,则返回默认值。
setattr(x,y,v)
setattr(x, 'y', v)就等于``x.y = v''
delattr(x,y)
delattr(x, 'y')就等于``del x.y''

class BlackMedium:
    feature='Ugly'
    def __init__(self,name,addr):
        self.name=name
        self.addr=addr

    def sell_house(self):
        print('%s 黑中介卖房子啦,傻逼才买呢,但是谁能证明自己不傻逼' %self.name)
    def rent_house(self):
        print('%s 黑中介租房子啦,傻逼才租呢' %self.name)

b1=BlackMedium('万成置地','回龙观天露园')

#检测是否含有某属性
print(hasattr(b1,'name'))
print(hasattr(b1,'sell_house'))

#获取属性
n=getattr(b1,'name')
print(n)
func=getattr(b1,'rent_house')
func()

# getattr(b1,'aaaaaaaa') #报错
print(getattr(b1,'aaaaaaaa','不存在啊'))

#设置属性
setattr(b1,'sb',True)
setattr(b1,'show_name',lambda self:self.name+'sb')
print(b1.__dict__)
print(b1.show_name(b1))

#删除属性
delattr(b1,'addr')
delattr(b1,'show_name')
# delattr(b1,'show_name111')#不存在,则报错

print(b1.__dict__)

True
True
万成置地
万成置地 黑中介租房子啦,傻逼才租呢
不存在啊
{'name': '万成置地', 'addr': '回龙观天露园', 'sb': True, 'show_name': <function <lambda> at 0x000001FCAFBD1E18>}
万成置地sb
{'name': '万成置地', 'sb': True}

__setattr__,__delattr__,__getattr__使用方法:

class Foo:
    x=1
    def __init__(self,y):
        self.y=y

    def __getattr__(self, item):
        print('----> from getattr:你找的属性不存在')


    def __setattr__(self, key, value):
        print('----> from setattr')
        # self.key=value #这就无限递归了
        # self.__dict__[key]=value #应该使用它

    def __delattr__(self, item):
        print('----> from delattr')
        # del self.item #无限递归了
        self.__dict__.pop(item)

#__setattr__添加/修改属性会触发它的执行
f1=Foo(10)
print(f1.__dict__) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值
f1.z=3
print(f1.__dict__)

#__delattr__删除属性的时候会触发
f1.__dict__['a']=3#我们可以直接修改属性字典,来完成添加/修改属性的操作
del f1.a
print(f1.__dict__)

#__getattr__只有在使用点调用属性且属性不存在的时候才会触发
f1.xxxxxx

----> from setattr
{}
----> from setattr
{}
----> from delattr
{}
----> from getattr:你找的属性不存在

授权:授权是包装的一个特性, 包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能。其它的则保持原样。授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。

实现授权的关键点就是覆盖__getattr__方法

import time
class FileHandle:
    def __init__(self,filename,mode='r',encoding='utf-8'):
        self.file=open(filename,mode,encoding=encoding)
    def write(self,line):
        t=time.strftime('%Y-%m-%d %T')
        self.file.write('%s %s' %(t,line))

    def __getattr__(self, item):
        return getattr(self.file,item)

f1=FileHandle('b.txt','w+')
f1.write('你好啊')
f1.seek(0)
print(f1.read())
f1.close()

2018-06-21 20:05:53 你好啊

__getattribute__使用:

class Foo:
    def __init__(self,x):
        self.x=x

    def __getattr__(self, item):
        print('执行的是我')
        # return self.__dict__[item]

f1=Foo(10)
print(f1.x)
f1.xxxxxx #不存在的属性访问,触发__getattr__

10
执行的是我
class Foo:
    def __init__(self,x):
        self.x=x

    def __getattribute__(self, item):
        print('不管是否存在,我都会执行')

f1=Foo(10)
f1.x
f1.xxxxxx

不管是否存在,我都会执行
不管是否存在,我都会执行
#_*_coding:utf-8_*_
__author__ = 'Linhaifeng'

class Foo:
    def __init__(self,x):
        self.x=x

    def __getattr__(self, item):
        print('执行的是我')
        # return self.__dict__[item]
    def __getattribute__(self, item):
        print('不管是否存在,我都会执行')
       # raise AttributeError('哈哈')

f1=Foo(10)
f1.x
f1.xxxxxx

不管是否存在,我都会执行
不管是否存在,我都会执行

#_*_coding:utf-8_*_
__author__ = 'Linhaifeng'

class Foo:
    def __init__(self,x):
        self.x=x

    def __getattr__(self, item):
        print('执行的是我')
        # return self.__dict__[item]
    def __getattribute__(self, item):
        print('不管是否存在,我都会执行')
        raise AttributeError('哈哈')

f1=Foo(10)
f1.x
f1.xxxxxx

不管是否存在,我都会执行
执行的是我
不管是否存在,我都会执行
执行的是我

#当__getattribute__与__getattr__同时存在,只会执行__getattrbute__,除非__getattribute__在执行过程中抛出异常AttributeError

描述符(__get__,__set__,__delete__)使用方法:

描述符协议;至少实现了__get__(),__set__(),__delete__()中的一个方法。

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

非数据描述符:未实现__set__()方法

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

#描述符Str
class Str:
    def __get__(self, instance, owner):
        print('Str调用')
    def __set__(self, instance, value):
        print('Str设置...')
    def __delete__(self, instance):
        print('Str删除...')

#描述符Int
class Int:
    def __get__(self, instance, owner):
        print('Int调用')
    def __set__(self, instance, value):
        print('Int设置...')
    def __delete__(self, instance):
        print('Int删除...')

class People:
    name=Str()
    age=Int()
    def __init__(self,name,age): #name被Str类代理,age被Int类代理,
        self.name=name
        self.age=age

p1=People('alex',18)

#描述符Str的使用
p1.name
p1.name='egon'
del p1.name

#描述符Int的使用
p1.age
p1.age=18
del p1.age

print(p1.__dict__)
print(People.__dict__)

print(type(p1) == People) #type(obj)其实是查看obj是由哪个类实例化来的
print(type(p1).__dict__ == People.__dict__)


Str设置...
Int设置...
Str调用
Str设置...
Str删除...
Int调用
Int设置...
Int删除...
{}
{'__module__': '__main__', 'name': <__main__.Str object at 0x0000026D51F0B550>, 'age': <__main__.Int object at 0x0000026D51F0BF98>, '__init__': <function People.__init__ at 0x0000026D51F6B510>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None}
True
True

注意事项:
一 描述符本身应该定义成新式类,被代理的类也应该是新式类
二 必须把描述符定义成这个类的类属性,不能为定义到构造函数中
三 要严格遵循该优先级,优先级由高到底分别是
1.类属性
2.数据描述符
3.实例属性
4.非数据描述符
5.找不到的属性触发__getattr__()

__setitem__,__getitem,__delitem__使用方法:

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

    def __getitem__(self, item):
        print(self.__dict__[item])

    def __setitem__(self, key, value):
        self.__dict__[key]=value
    def __delitem__(self, key):
        print('del obj[key]时,我执行')
        self.__dict__.pop(key)
    def __delattr__(self, item):
        print('del obj.key时,我执行')
        self.__dict__.pop(item)

f1=Foo('sb')
f1['age']=18
f1['age1']=19
del f1.age1
del f1['age']
f1['name']='alex'
print(f1.__dict__)

del obj.key时,我执行
del obj[key]时,我执行
{'name': 'alex'}

__str__,__repr__,__format__使用方法:

改变对象的字符串显示__str__,__repr__
自定制格式化字符串__format__

#_*_coding:utf-8_*_
__author__ = 'Linhaifeng'
format_dict={
    'nat':'{obj.name}-{obj.addr}-{obj.type}',#学校名-学校地址-学校类型
    'tna':'{obj.type}:{obj.name}:{obj.addr}',#学校类型:学校名:学校地址
    'tan':'{obj.type}/{obj.addr}/{obj.name}',#学校类型/学校地址/学校名
}
class School:
    def __init__(self,name,addr,type):
        self.name=name
        self.addr=addr
        self.type=type

    def __repr__(self):
        return 'School(%s,%s)' %(self.name,self.addr)
    def __str__(self):
        return '(%s,%s)' %(self.name,self.addr)

    def __format__(self, format_spec):
        # if format_spec
        if not format_spec or format_spec not in format_dict:
            format_spec='nat'
        fmt=format_dict[format_spec]
        return fmt.format(obj=self)

s1=School('oldboy1','北京','私立')
print('from repr: ',repr(s1))
print('from str: ',str(s1))
print(s1)

'''
str函数或者print函数--->obj.__str__()
repr或者交互式解释器--->obj.__repr__()
如果__str__没有被定义,那么就会使用__repr__来代替输出
注意:这俩方法的返回值必须是字符串,否则抛出异常
'''
print(format(s1,'nat'))
print(format(s1,'tna'))
print(format(s1,'tan'))
print(format(s1,'asfdasdffd'))

from repr:  School(oldboy1,北京)
from str:  (oldboy1,北京)
(oldboy1,北京)
oldboy1-北京-私立
私立:oldboy1:北京
私立/北京/oldboy1
oldboy1-北京-私立

__slots__使用方法:

'''
1.__slots__是什么:是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)
2.引子:使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)
3.为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__,当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给实例添加新的属性了,只能使用在__slots__中定义的那些属性名。
4.注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,你应该只在那些经常被使用到 的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象 。
关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。更多的是用来作为一个内存优化工具。

'''
class Foo:
    __slots__='x'


f1=Foo()
f1.x=1
# f1.y=2#报错
print(f1.__slots__) #f1不再有__dict__

x

class Foo:
    __slots__=['name','age']

f1=Foo()
f1.name='alex'
f1.age=18
print(f1.__slots__)

f2=Foo()
f2.name='egon'
f2.age=19
print(f2.__slots__)

print(Foo.__dict__)
#f1与f2都没有属性字典__dict__了,统一归__slots__管,节省内存

['name', 'age']
['name', 'age']
{'__module__': '__main__', '__slots__': ['name', 'age'], 'age': <member 'age' of 'Foo' objects>, 'name': <member 'name' of 'Foo' objects>, '__doc__': None}

不能添加新的属性,只能使用已有属性(添加属性,本质在操作属性字典,属性字典已经不存在了)

__next__和__iter__实现迭代器协议:

#_*_coding:utf-8_*_
__author__ = 'Linhaifeng'
class Foo:
    def __init__(self,x):
        self.x=x

    def __iter__(self):
        return self

    def __next__(self):
        n=self.x
        self.x+=1
        if self.x == 7:
            raise StopIteration
        return self.x

f=Foo(3)
for i in f:
    print(i)

4
5
6

__doc__:调用该方法,显示描述信息,该属性无法被继承。

__del__方法:

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

class Foo:

    def __del__(self):
        print('执行我啦')

f1=Foo()
del f1
print('------->')

执行我啦
------->

class Foo:

    def __del__(self):
        print('执行我啦')

f1=Foo()
# del f1
print('------->')

------->
执行我啦

__call__方法:

对象后面加括号,触发执行

class Foo:

    def __init__(self):
        pass
    
    def __call__(self, *args, **kwargs):

        print('__call__')


obj = Foo() # 执行 __init__
obj()       # 执行 __call__

__call__

__enter__和__exit__使用方法:

上下文管理协议:

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

    def __enter__(self):
        print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
        # return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with中代码块执行完毕时执行我啊')


with Open('a.txt') as f:
    print('=====>执行代码块')
    # print(f,f.name)

出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量
=====>执行代码块
with中代码块执行完毕时执行我啊

执行with Open('a.txt') as f:可以理解为f = Open.__enter__(),把Open.__enter__()的返回值,赋值给f。

with语句代码块执行完之后,执行__exit__(),再接着往下执行。

若with内没遇到异常,则正常执行,exc_type, exc_val, exc_tb且三个值都为None。

若with内遇到异常,1.__exit()__内,没有return True,吐出异常,正常中断

          2.__exit()__内,有return True,吞下异常

这两种情况,exc_type, exc_val, exc_tb的值都不为None,分别是错误的类型,错误的值,错误的追踪信息。

posted @ 2018-06-21 20:56  Liu呵呵  阅读(128)  评论(0编辑  收藏  举报