python类的学习笔记(二)

isinstance和issubclass

反射setattr、delattr、getattr、hasattr
__str__和__repr__


item系列__getitem__、__setitem____delitem__


__del____new____call__

with和__enter__、__exit__

__len__

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

    class Foo(object):
        pass

    obj = Foo()
    print(isinstance(obj, Foo))

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

class Foo(object):
    pass
class Bar(Foo):
    pass
print(issubclass(Bar, Foo))

反射

python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)

class People:
    f = '类的静态变量'
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def say_hi(self):
        print('hi,%s'%(self.name))

obj = People('wy',22)

# 检测是否含有某属性 hasattr
print(hasattr(obj,'name'))
print(hasattr(obj,'say_hi'))
# 获取属性 getattr
n = getattr(obj,'name')
print(n)
s = getattr(obj,'say_hi')
s()
print(getattr(obj,'aaa','不存在'))
# 设置属性 setattr
print(obj.__dict__)
setattr(obj,'sex','male')
print(obj.__dict__)
# 删除属性 delattr
delattr(obj,'name')
print(obj.__dict__)
# delattr(obj,'nn')  不存在则报错

__str__和__repr__方法

__str__是在str()函数被使用,或是在print函数打印一个对象的时候才被调用的,并且它返回的字符串对终端用户更友好。

如果只想实现这两个特殊方法中的一个,__repr__是更好的选择,因为如果一个对象没有__str__函数,而Python又需要调用它的时候,解释器会用__repr__作为替代。

__repr__和__str__这两个方法都是用于显示的,__str__是面向用户的,而__repr__面向程序员。

打印操作会首先尝试__str__和str内置函数(print运行的内部等价形式),它通常应该返回一个友好的显示。

__repr__用于所有其他的环境中:用于交互模式下提示回应以及repr函数,如果没有使用__str__,会使用print和str。它通常应该返回一个编码字符串,可以用来重新创建对象,或者给开发者详细的显示。

当我们想所有环境下都统一显示的话,可以重构__repr__方法;当我们想在不同环境下支持不同的显示,例如终端用户显示使用__str__,而程序员在开发期间则使用底层的__repr__来显示,实际上__str__只是覆盖了__repr__以得到更友好的用户显示。

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('qinghua','北京','私立')
print(s1)
# 这里直接打印s1,如果有__str__,则打印__str__方法,没有则会打印__repr__方法
''' 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'))
class B:

     def __str__(self):
         return 'str : class B'

     def __repr__(self):
         return 'repr : class B'


b=B()
print('%s'%b)
print('%r'%b)

item系列__getitem__/__setitem__/__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 obj.key时,我执行 del f1['age'] # del obj[key]时,我执行 f1['name']='alex' print(f1.__dict__)

__getitem__

#如果类把某个属性定义为序列,可以使用__getitem__()输出序列属性中的某个元素.
class
FruitShop(): def __getitem__(self,i): return self.fruits[i]#可迭代对象 if __name__ == "__main__": shop = FruitShop() print(shop) #__main__.FruitShop instance shop.fruits = ["apple", "banana"] print(shop[1]) #banana for item in shop: print(item)

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

__new__ :

1.创建类时先执行type的__init__方法,
2.当一个类实例化时(创建一个对象)执行type的__call__方法,__call__方法的返回值就是实例化的对象
    __call__内部调用
      -类.__new__方法,创建一个对象
      -类.__init__方法,初始化对象

实例化对象是谁取决于__new__方法,__new__返回什么就是什么

 __new__() 方法的特性:
  • __new__() 方法是在类准备将自身实例化时调用。
  • __new__() 方法始终都是类的静态方法,即使没有被加上静态方法装饰器
class Foo(object):
    pass

obj=Foo()  #默认是调用该类的直接父类的__new__()方法来构造该类的实例
print(obj) #打印结果:<__main__.Foo object at 0x000002636FEAA208>

事实上如果(新式)类中没有重写__new__()方法,即在定义新式类时没有重新定义__new__()时,Python默认是调用该类的直接父类的__new__()方法来构造该类的实例,
如果该类的父类也没有重写__new__(),那么将一直按此规矩追溯至object的__new__()方法,因为object是所有新式类的基类。

class F1(object):
  #重写__new__方法,返回这个重写的__new__方法
    def __new__(cls, *args, **kwargs):
        return 123

obj=F1() #实例化对象是谁取决于__new__方法,__new__返回什么就是什么
print(obj,type(obj))  #打印结果:123 <class 'int'>


class F2(object):
    pass

class F3(object):
    def __new__(cls, *args, **kwargs):
        return F2()

obj=F3()    #实例化对象是谁取决于__new__方法,__new__返回什么就是什么
print(obj)  #<__main__.F2 object at 0x00000210119BA4A8>



如果要得到当前类的实例,应当在当前类中的 __new__() 方法语句中调用当前类的父类的 __new__() 方法。
例如,如果当前类是直接继承自 object,那当前类的 __new__() 方法返回的对象应该为:


def __new__(cls, *args, **kwargs):
    ...
    return object.__new__(cls)



__new__至少要有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供


__new__必须要有返回值,返回实例化出来的实例,这点在自己实现__new__时要特别注意,可以return父类__new__出来的实例,或者直接是object的__new__出来的实例


__init__有一个参数self,就是这个__new__返回的实例,__init__在__new__的基础上可以完成一些其它初始化的动作__init__不需要返回值

__new__实现单例模式

class Singleton:
    def __new__(cls, *args, **kw):
        if not hasattr(cls, '_instance'):
            cls._instance = object.__new__(cls)
        return cls._instance

__call__

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

注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()

class Foo:

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

        print('__call__')


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

with和__enter__,__exit__

class A:
    def __enter__(self):
        print('before')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('after')


with A() as a:
    print('123')

class A:
    def __init__(self):
        print('init')

    def __enter__(self):
        print('before')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('after')


with A() as a:
    print('123')

pickle模块

用于序列化的两个模块
  json:用于字符串和Python数据类型间进行转换
  pickle: 用于python特有的类型和python的数据类型间进行转换
  json提供四个功能:dumps,dump,loads,load
  pickle提供四个功能:dumps,dump,loads,load

pickle可以存储什么类型的数据呢?

  1. 所有python支持的原生类型:布尔值,整数,浮点数,复数,字符串,字节,None。
  2. 由任何原生类型组成的列表,元组,字典和集合。
  3. 函数,类,类的实例

 pickle模块可能出现三种异常:

    1. PickleError:封装和拆封时出现的异常类,继承自Exception

    2. PicklingError: 遇到不可封装的对象时出现的异常,继承自PickleError

    3. UnPicklingError: 拆封对象过程中出现的异常,继承自PickleError

import pickle
class A:
    def __init__(self,name):
        self.name = name
    def test(self):
        print('%s是帅逼!'%(self.name))

obj = A('wy')
print(obj.name)
with open('test','wb') as f:
    obj_d = pickle.dumps(obj)
    print(obj_d)
    f.write(obj_d)

with open('test','rb') as f:
    obj_a = f.read()
    print(obj_a)

    obj_l = pickle.loads(obj_a)
    print(obj_l.name)
    print(obj_l.test())

__len__

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

    def __len__(self):
        return len(self.__dict__)
a = A()
print(len(a))

# 输出: 2

 

参考文献:

  python中的__new__方法

  面向对象进阶

原文地址:https://www.cnblogs.com/python-Arvin/p/11889446.html