Fay

面向对象 ,特殊成员和魔法方法

特殊成员,类的双下__方法:

一: isinstance 和 issubclass

class Foo(object):
    pass

obj = Foo()

print(isinstance(obj, Foo))  #判断这个类,是不是这个对象的实例



class Foo(object):
   pass

class Bar(Foo):
    pass

print(issubclass(Bar, Foo)) #判断子类与父类的继承关系

二: ==item系列: getitem, setitem, delitem

class Foo:

    def __getitem__(self, item): #这个item是直接就出来的,必须要有这个
        print('getitem  a ')
        return self.__dict__[item] #self是指f1实例化的自己,__dict__是指存在那个空间里的字典,
        						 这个item就是传进来的'name'字符串形式.
    def __setitem__(self, key, value):
        print('setitem  b ')
        self.__dict__[key] = value   

    def __delitem__(self, key):
        print('delitem  c')
        self.__dict__.pop(key)#
f1 = Foo()
print(f1.__dict__) #开始的时候__dict__里面并没有存放任何的值


# # 赋值操作
f1['name'] = 'egon'#相当于字典的赋值,就直接触发__setitem__方法,还需要再__setitem__里面设置一下, self.__dict__[key] = value这样就将['name'] = 'egon'赋值进去了
print(f1.__dict__)  #此时打印的结果就为 setitem  b   {'name': 'egon'}

f1['age'] = 18  #再来增加一个
print(f1.__dict__)  #{'name': 'egon', 'age': 18}

# # 删除操作
del f1['age'] #同样需要删除时,就直接触发__delitem__方法,还需要再__delitem__里面设置一下,self.__dict__.pop(key),这样字典age这个key对于的value值就被删除了
print(f1.__dict__)   #{'name': 'egon'}

# 访问,获取
print(f1['name']) #以对象和字典key的结合方式,触发__getitem__方法. 还需要再__getitem__里面设置一下, return self.__dict__[item],这样就通过字典的形式访问到值

# 总结:
# 所以item系列是有访问,赋值,删除的基本功能的.但是它具有有个特点是必须要通过字典的方式来操作.

#  而__hasattr__,__getattr__,__setattr__,__delattr__  这attr系列也同样具备访问,赋值,删除的基本功能的.
# 但是attr系列是 通常叫反射:是通过字符串映射到对象的属性,是通过字符串访问到类,对象属性的一种方法,注意区分


# 点的方式操作属性就是跟getattr相关,中括号的方式操作属性就是跟item相关

三: attr系列: getattr, setattr, delattr

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__只有在使用点调用属性

四:___getattr__和 _getattribute

1.getattr :单独作为的方法使用时,是调用不存在的属性时,会自动触发getattr方法,并运用getattr里的方法.
class Foo:
    def __init__(self,x):
        self.x=x

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

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

2.getattribute:


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

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

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

3.俩者同时出现:

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

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

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

f1=Foo1(10)

f1.xxxxxx1  #当__getattribute__与__getattr__同时存在,只会执行__getattrbute__

4.先执行__getattribute__

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

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

f1.xxxxxx2
打印结果:
不管是否存在,我都会执行
执行的是我

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

五: __repr__方法和 __str__方法

repr_与__str__都是用来.控制输出de,输出的结果是return的值,此时return的值必须为字符串的形式

repr方法

class B:
  def __init__(self,age):
    self.age = age
  def __repr__(self ):
    return self.age
    
b = B('18')
print(b.__dict__)  #{'age': '18'}
print( b)  #repr(b)---> b.__repr__() #意思是说在方法里面有__repr__方法之后,你实例化一个对象,会自动触发这个repr方法

__str__方法

class People:
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def __str__(self):
        return  self.name #返回一个值
        # return  'name:%s ,age:%s' % (self.name,self.age)  #返回俩个值
obj1 = People('egon',18)
print(obj1)
# print(obj1.__dict__) #{'name': 'egon', 'age': 18}
#这样打印对象的时候,就自动触发__str__方法,将obj对象里面的值调出来
# 如果不加__str__方法,直接打印对象会<__main__.People object at 0x00000000027FEBE0>,只是一个内存地址而已
# 加了__str__方法后,就会直接打印通过对象直接打印他的值

那么要是俩种方法都同时存在于呢,优先级是哪个,看例1:

class B:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __repr__(self):
        return '第三季度涉及到'

    def __str__(self ):
        return self.age

f2 = B('egon','18')#必须为字符串
print(f2)
# !!!__repr_与__str__都是用来.控制输出de,输出的结果是return的值,此时return的值必须为字符串的形式
# str函数或者print函数--->obj.__str__()
# repr或者交互式解释器--->obj.__repr__()
# 如果__str__没有被定义,那么就会使用__repr__来代替输出
# 注意:这俩方法的返回值必须是字符串,否则抛出异常

# #实例化对象的时候,都必须为字符串
# # 一般情况下建议用repr

例2:

class Course:#创建一个课程类,他有自己的属性,但是创建课程这个动作是管理员来完成的,所以是由管理员来做这个事情
    def __init__(self, name, price, period, kind):
        self.name = name
        self.price = price
        self.period = period
        self.kind = kind
    def __repr__(self):
        s = ''
        for k in self.__dict__:  #循环这个课程类的信息,他存在内存的dict字典
            s += '%s:%s\n'%(k,self.__dict__[k])
        return s
print(Course)  #他会自动调用__reor__方法

六:format

1.用法1

class Date:
    def __init__(self,mon,day,year):

        self.year = year
        self.mon = mon
        self.day = day


    def __format__(self, format_spec): #自动出现format_spec

        return '{0.year}{0.mon}{0.day}'.format(self)

d1= Date(2018,8,8)
print(d1) #<__main__.Date object at 0x00000000027FD940>
print(format(d1))  # 820188

return self.day  #这样写是错误的
ypeError: __format__ must return a str, not int会报错,是因为return的是必须是字符串,而self.day,实例化的是int类型
ormat_dict1 = {
    'ymd':'{0.year}{0.mon}{0.day}',
    'm-d-y':'{0.mon}:{0.day}:{0.year}',
    'y-m-d':'{0.year}-{0.mon}-{0.day}'
}  #定义这样三种格式的字典

class Date:
    def __init__(self,mon,day,year):
        self.mon = mon
        self.day = day
        self.year = year

    def __format__(self, format_spec): #如果下面不传参数的话,那format_spec就会变成空格也不会报错,但是要是传参数呢
        fm = format_dict1[format_spec]  #如果他的选择是'ymd'的话,fm就等于是从format_dict的字典里面,去选择.取到最终的格式而ymd==format_spec
        return fm.format(self)  #就返回fm里面的格式


d2= Date(2018,8,8)
print(format(d2,'ymd')) #用户如果传的是ymd,那么就传给了format_spec ,即ymd==format_spec

七: len

class A:
    def __init__(self):
        self.a = 1
        self.b = 2

    def __len__(self):
        return len(self.__dict__)
a = A()
print(len(a))
#类内部定义了__len__方法后,打印对象的长度.eg:print(len(对象名))就触发内部的__len__方法.

八: call,对象后面加括号,触发执行,执行时调用这个类下面的__cal__l方法==

class Foo:
    def __init__(self):
        pass
    def __call__(self, *args, **kwargs):
        print('__call__',pppp)

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

#类内部定义__call__方法后,直接实例化一个对象后,直接用对象名(),
就可以触发__call__,打印__call__里的所有信息

九:moduleclass

  module 表示当前操作的对象在那个模块

  class 表示当前操作的对象的类是什么

class C:

    def __init__(self):
        self.name = ‘SB'
from lib.aa import C

obj = C()
print obj.__module__  # 输出 lib.aa,即:输出模块
print obj.__class__      # 输出 lib.aa.C,即:输出类

十:__del 析构方法只在实例被删除的时候才会触发__del__方法

f  = open('txt文件名')
f.read()
f.close()  回收操作系统的资源
# 拿到一个f 的对象的话,涉及到俩方面的资源
# 1.如果是一个赋值,那一定是应用程序的资源
# 2.后边的是一个操作系统的资源,在程序关闭之前把操作系统的资源关闭掉
# 就是我们在学习文件操作的时候一定要记得关闭文件,如果程序结束了,操作系统的资源即文件没有关掉,那么这资源就剩下了,就占内存空间
对于# __del__方法的引导
'''
# __del__方法,具体如下
# class Open:
#     def __init__(self,filename):
#         print('open file...')  #模拟打开文件
#         self.filename = filename
#
#     def __del__(self):
#         print('回收操作系统资源')
#
# f = Open('settings.py') #f是变量
#
# print('======表示程序结束====')

'''
open file...
======表示程序结束====
回收操作系统资源

看打印的顺序,简单结论说:
当f这个实例化对象去执行文件打开操作的时候,就模拟打开文件了,所以打印 open file...[就是去打开一个文件执行操作]
然后打开文件后,执行了相关的操作之后,句模拟打印======表示程序结束====,程序结束之后,程序自动的会回收f这个变量,但是遗留下文件资源
但是这个文件还没有关闭呀,所以,造类里面设置了__del__之后就自动的将文件关闭,这样一个功能,为了节省内存空间嘛

老师总结:在对象被删除的时候,会自动先触发__del__方法,再把对象删掉

十一:迭代器协议: _iternext

例子:说明类对象得到一个数,通过内置的迭代器方法,将得到的数进行迭代输出,并设置到一定数之后就停止输出
class Bar:
    def __init__(self,a):
        self.a = a

    def __iter__(self):
        return self

    def __next__(self): #执行一次就返回前俩个的和
        self.a += 1 #让a得到的数字17进行迭代+1,或者说是进行累加1
        if self.a == 21: #如果a得到的数等于21
            raise StopIteration('终止了') #就停止+1的操作,抛出异常,这个异常并不是报错,只是跟程序本身说停止叠加1的操作,并不是真的报错
        return self.a

c = Bar(17)
for i in c:
    print(i)



把一个类对象变成一个可迭代的,且类里面必须要有__iter__方法,表示可迭代,要知道光可以迭代并没有实际用处,
根据之前学习迭代器的理论,得知,next是取下一个值的,所以此时运用到类对象的时候,就提供了相应的内置方法,__next__来取值.

实例1.实现斐波那契数列

class Fib:
    def __init__(self):
        self.a = 1
        self.b = 1

    def __iter__(self):
        return self

    def __next__(self):
        if self.a > 100:
            raise StopIteration('可以停止了')
        self.a, self.b = self.b, self.a + self.b  # 主要在这里,结合a,b = b, a理解,等于一个交换值
        return self.a
f1 = Fib()
print(next(f1))
print(next(f1))
print(next(f1))
print(next(f1))
print(next(f1))
print('===================================')

for i in f1:
    print(i) #如果单纯这样写出来的结果就是无限循环的数列.所以需要再__next__方法里面加上判断

十二: 单例模式: new

创造一个对象,一个类始终只有一个实例
# __new__: 单例模式,创造一个对象,一个类始终只有一个实例
#             当你第一次实例化这个类的时候,就创建一个实例化的类,当你再来			 实例化的时候,就用之前创建的对象
#        (如果之前已经有创建一个对象了,就不再创建了,会在__new__里去控制
          ,如果你来一个新的不同的对象,你来一个我就new一个,可以理解为创造一个具体的实物手机,你来一个实物手机我就创建一个实物手机,但你要在同一个实物手机的基础上做不同的处理,比如换手机壳的操作,手机壳只能戴一个,你换各种颜色的手机壳可以,但是你只能讲前面一个手机壳去掉,这里就理解为覆盖掉)

class A:

    _instance = False
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def __new__(cls, *args, **kwargs):
        if not cls._instance: #如果没有创建这个对象
            cls._instance = object.__new__(A) #就去创建一个新的对象
            return cls._instance #将创建的对象返回

        else: #如果有这个对象,就不再创建新的对象,且如果有不同对象有相同属性的话,那么后一个对象的属相会覆盖前一个对象相同的属性,相当于相同属性之间,只有一块内存空间
            return cls._instance




egon = A('egon',18)
nezha = A('nezha',25)

print(nezha)
print(egon)

print(egon.name) #nezha
print(nezha.name)#nezha  不同对象会覆盖前一个相同属性的值,

egon.cloth = '衣服'
print(nezha.cloth)
print(egon.cloth)

十三: eq

判断俩个对象的属性的值是否相等,用==去触发__eq__的执行

class A:
    def __init__(self):
        self.a = 1
        self.b = 2

    def __eq__(self,obj):
        if  self.a == obj.a and self.b == obj.b:
            return True
a = A()
b = A()
print(a == b)
有__eq__,判断的是俩个对象的属性是否相等
无__eq__,判断的是内存地址

十四: __hash__判断一个对象是否可哈希,且是根据内存地址去哈希的

class A:
    def __init__(self):
        self.a = 1
        self.b = 2

    def __hash__(self):
        return hash(str(self.a)+str(self.b))
a = A()
print(hash(a))

posted on 2018-09-30 00:52  sunny7  阅读(162)  评论(0编辑  收藏  举报

导航