python对象判等的特殊方法_python(四):面型对象--类的特殊方法

一、跟实例创建和执行有关的

__new__、__init__、__call__.

类加括号调用了__init__方法来创建一个实例对象。这一过程分成了两步: 类调用__new__来创建实例对象,__new__调用__init__来初始化实例对象。

classA:

count=0def __init__(self):print("__init__ has called. 2")def __new__(cls):print("__new__ has called. 1")return object.__new__(cls)def __call__(cls):print("__call__ has called. 3")

a=A()

a()"""__new__ has called. 1

__init__ has called. 2

__call__ has called. 3"""

类的__new__()方法很少通过用户代码定义。如果定义了它,它通常是用原型__new__(cls, *args, **kwargs)编写的。其中args和kwargs与传递给__init__()的参数相同。__new__()始终是一个类方法,接受类对象作为第一个参数。尽管__new__()会创建一个实例,但它不会自动地调用__init__()。实例对象加括号会调用__call__方法,一般作为程序的入口,并且可以在这一步修改实例属性或调用实例方法。

classObj(object):def __init__(self, name, price):

self.name=name

self.__price =pricedef __say(self):print("{}, {}.".format(self.name, self.__price))def __call__(self, discount):

self.__price = discount * self.__priceself.__say()

apple= Obj("apple", 10.0)

apple(0.8)

二、跟实例属性有关的

__getattribute__,  __getattr__,  __setattr__, __delattr__、__getitem__、__setitem__、__delitem__、__missing__

在__init__时,会调用__setattr__初始化实例属性,在__dict__添加键值对。

classSample:def __init__(self, name, age):

self.name=name

self.age=agedef __setattr__(self, key, value):print("__setattr__ has called.")try:

self.__dict__[key] =valueexcept:

self.__dict__ ={key: value}

sam= Sample("Li", 24)#__setattr__ has called.#__setattr__ has called.

对实例属性的访问时,

1.首先执行__getattribute__方法(这又是一个特殊方法),去调用object基类的__getattribute__来查询__dict__里的键值对。如果存在则直接返回,相当于执行了member.__dict__.get(obj),如果不存在则调用__getattr__方法。

2.__getattr__方法会在实例内存空间中尽可能的搜索该属性值,如果搜索到则直接返回,搜索不到会抛出AttributeError。

注意下面两段代码的区别:__getattribute__的return只是一个硬编码的字符串而不是属性值,__getattr__的return则是实例的属性值。因此,如果有需求,一般会在__getattr__里写需求代码。

classMemberCounter:def __init__(self, name, age):

self.name=name

self.age=agedef __getattribute__(self, obj):print("__getattribute__ is called.")return object.__getattribute__(self, obj)def __getattr__(self, obj):print("__getattr__ is called.")return "{} Not assgined.".format(obj)

member= MemberCounter("An", 24)print(member.name)print("\n------------------\n")print(member.gender)"""__getattribute__ is called.

An

------------------

__getattribute__ is called.

__getattr__ is called.

gender Not assgined."""

classMemberCounter:def __init__(self, name, age):

self.name=name

self.age=agedef __getattribute__(self, obj):if obj == "gender":return "male"

else:return object.__getattribute__(self, obj)

member= MemberCounter("An", 24)print(member.gender)

member.gender= "female"

print(member.gender)print(member.__dict__)"""male

male

{'name': 'An', 'age': 24, 'gender': 'female'}"""

__getattribute__

classMemberCounter:def __init__(self, name, age):

self.name=name

self.age=agedef __getattr__(self, obj):if obj == "gender":return "male"

else:raiseAttributeError

member= MemberCounter("An", 24)print(member.gender)

member.gender= "female"

print(member.gender)print(member.__dict__)"""male

female

{'name': 'An', 'age': 24, 'gender': 'female'}"""

__getattr__

当访问一个属性的时候:

解释器首先在实例的字典中搜索,

若找不到则去创建这个实例的类的字典中搜索,

若还找不到就到类的基类中搜索,

如果还不找不到最后会尝试调用类的__getattr__方法来获取属性值(若类中定义了该方法的话).

如果这个过程也失败,则引发AttributeError异常

在使用member.name = "Wei"和del member.age时,实际上调用了__setattr__和__delattr__。这两个函数没有类似__getattribute__的使用规则。即无论何时给属性赋值,都会调用 __setattr__() 方法;无论何时删除一个属性,都将调用 __delattr__() 方法。

classMemberCounter:def __init__(self, name, age):

self.name=name

self.age=agedef __setattr__(self, old, new):print("\033[1;36m {} = {} \033[0m has been created.".format(old, new))

object.__setattr__(self, old, new)def __delattr__(self, key):print("{} is deleted.".format(key))del self.__dict__[key]def __getattr__(self, item):return self.__dict__[item] #object.__getattr__(self, item)

member= MemberCounter("An", 24)print(member.__dict__)delmember.ageprint(member.__dict__)"""name = An has been created.

age = 24 has been created.

{'name': 'An', 'age': 24}

age is deleted.

{'name': 'An'}"""

classA:def __init__(self, *args):

self.name, self.age=argsdef __getitem__(self, key):print("__getitem__ has called.")

self.__dict__.get(key, ValueError)def __setitem__(self, key, value):print("__setitem__ has called.")

self.__dict__.update({key: value})def __delitem__(self, key):print("__delitem__ has called.")del self.__dict__[key]

a= A("Li", 27)print(a.__dict__)

a["name"]print(a.__dict__)

a["name"] = "Alex"

print(a.__dict__)del a["age"]print(a.__dict__)"""{'name': 'Li', 'age': 27}

__getitem__ has called.

{'name': 'Li', 'age': 27}

__setitem__ has called.

{'name': 'Alex', 'age': 27}

__delitem__ has called.

{'name': 'Alex'}"""

序号目的所编写代码Python 实际调用

5

列出所有属性和方法

dir(x)

序号目的所编写代码Python 实际调用

3

删除一个键值对

del x[key]

三、实例属性的保存格式---__slots__和__dict__

实例对象通常以字典的形式保存属性, 也即__dict__。这种格式可以被__slots__修改 。下面是从各个python书籍中摘下的关于__slots__描述。

定义__slots__后,可以在实例上分配的属性名称将被限制为指定的名称,否则将引发AttributeError异常。

这种限制可以阻止其他人向现有实例添加新属性,解决了用户将值分配给他们无法正确拼写的属性时出现的异常。

在实际使用时,__slots__从未被当做一种安全的特性来实现。它实际上是对内存和执行速度的一种性能优化。

使用__slots__的类的实例不再使用字典来存储实例数据。相反,会使用基于数组的更加紧凑的数据结构。

在会创建大量对象的程序中,使用__slots__可以显著减少内存占用和执行时间。

__slots__与集成的配合使用需要一定的技巧。如果类继承自使用__slots__的基类,那么它也需要定义__slots__来存储自己的属性(即使它不会添加任何属性)。这样才能充分利用__slots__提供的优势。如果忘记了这一点,派生类的运行速度将更慢,占用的内存也比未在任何类上使用__slots__时多。

__slots__的使用也可以使代码不必要求实例具有底层__slots__属性。尽管这一点通常不适用于用户代码。但可以编写其他支持对象实用工具库和其他工具。依靠__dict__来调试、序列化对象并执行其他操作。

__slots__的存在不会对__getattribute__()、__getattr__()、和__setattr__()等方法的调用产生任何影响。因为这些方法应该在类中重新定义。但是,这些方法的默认行为将考虑到__slots__。

此外,没用必要向__slots__添加方法或特性名称,因为它们存储在类中,而不是存储在每个实例中。

classMemberCounter:__slots__ = ("name", "age")#注意, ()指定存储格式为元组,"name"和"age"严格限制了实例化对象的参数,不可随意增加,但可以删除

def __init__(self, name, age): #不可再写*args, **kwargs

self.name =name

self.age=age

member= MemberCounter("An", 24, )#member.__dict__ # 当__slots__被指定时,覆盖了__dict__

print(member.__slots__)print(member.name, member.age)#member.gender = "female" 报错#('name', 'age')#An 24age"

如何在定义了__slots__,来储存dict或者list等数据呢?以下面的例子为例:

classLocal(object):__slots__ = ('__dict__', '__list__')def __init__(self, name, age, gender):

object.__setattr__(self, "__dict__", {}) # 注意,__dict__和__list__只是我随意写的,你可以通篇把__dict__改成dict,照样没有关系

object.__setattr__(self, "__list__", []) # 也就是说,__dict__只是一个属性链接,它从__slots__中链接到一个字典对象并提供相关操作

self.name=name # 假定你已经知道self.name = name 实际上式调用了__setattr__方法,也就是自动存到了self.__dict__里

self.age=age

self.gender=genderdef __setattr__(self, key, value):try:

self.__dict__[key] =valueexceptKeyError:

self.__dict__ ={key: value}def __getattr__(self, name):try:return self.__dict__[name]exceptKeyError:raiseAttributeError(name)def __delattr__(self, name):try:del self.__dict__[name]exceptKeyError:raiseAttributeError(name)

local= Local("Li", 24, "famle")print(local.__slots__)print(local.__dict__)

# 0('__dict__', '__list__')

# {'name': 'Li', 'age': 24, 'gender': 'famle'

四、跟运算有关的

__eq__(==)、__ge__(>=)、__gt__(>)、__le__ (<=)、__lt__ (

参见: http://old.sebug.net/paper/books/dive-into-python3/special-method-names.html

classA:def __init__(self, number):

self.number=numberdef __gt__(self, obj):print("__gt__ has called.")return self.number >obj.numberdef __ge__(self, obj):print("__ge__ has called.") #既然执行了这个函数,你可以尽情的在return之前写想写的功能

return self.number >=obj.number

a= A(23) #当你写 23 > 45时,实际等同于 a = int(23), b = int(45); a > b

b = A(45) #这里A就相当于int

c = A(45)print(a >b)print(b >=c)"""__gt__ has called.

False

__ge__ has called.

True"""

此外,还有原地操作:

序号目的所编写代码Python 实际调用

10

原地按位 and

x &= y

11

原地按位 xor

x ^= y

12

原地按位 or

x |= y

以及一些自身简单运算:

序号目的所编写代码Python 实际调用

6

整数转换

int(x)

8

四舍五入至最近的整数

round(x)

9

四舍五入至最近的 n 位小数

round(x, n)

10

>= x 的最小整数

math.ceil(x)

11

<= x的最大整数

math.floor(x)

12

对 x 朝向 0 取整

math.trunc(x)

五、跟字符串有关的

__str__、__repr__、__format__、[__bytes__]

这些都是跟字符串格式和字符串表示有关的特殊方法。

classCls(object):def __init__(self):

self.name= "alex"self.age= 27self.gender= "female"

def __str__(self):print("__str__ has called.")return "Str: my name is {}, {}.".format(self.name, self.age)def __format__(self, special):print("__format__ has called.")if special == "":return self.__str__() #也可以写成str(self)

for _, v in self.__dict__.items(): #实际上是{"name": "alex", "age": 27}

special = special.replace("%s", str(v), 1)return special

观察下面二段代码打印的结果:

cls =Cls()

str1= "{:%s, %s, %s}".format(cls)print("-------------")print(str1)"""__format__ has called.

-------------

alex, 27, female"""

cls =Cls()

str2= format(cls, "my name is %s, %s, %s.")print("-------------")print(str2)"""__format__ has called.

-------------

my name is alex, 27, female."""

以上两端代码本质都是一样的,即format和一个字符串匹配,也就是__format__函数传递了special参数(字符串)。

当format函数没有和字符串匹配,也就是__format__函数没有传入参数special时,会调用__str__函数。

cls =Cls()

str3=format(cls)print("-------------")print(str3)"""__format__ has called.

__str__ has called.

-------------

Str: my name is alex, 27."""

str(cls)调用了__str__方法。str(cls)调用__str__方法, string.format(cls)和format(cls)调用__format__方法。

cls =Cls()print(str(cls))"""__str__ has called.

Str: my name is alex, 27."""

再来看__str__和__repr__的区别和联系。当__str__存在时,print(obj)和str(obj)实际调用了__str__方法;当__str__不存在时,就会调用__repr__。但是当定义了__repr__时,obj本身会返回一个"合法"的字符串表达式,而不再返回一个描述符,当然这个描述符也可以被__str__修改,只是会被__repr__覆盖。

classA:def __str__(self):print("__str__ has called.")return self.__class__.__name__ + "__str__"a=A()

a#<__main__.a at>

print(a)#__str__ has called.#A__str__

str(a)#__str__ has called.#A__str__

classA:def __repr__(self):print("__repr__ has called.")return self.__class__.__name__ + "__repr__"a=A()

a#__repr__ has called.#A__repr__

print(a)#__repr__ has called.#A__repr__

str(a)#__repr__ has called.#A__repr__

classA:def __str__(self):print("__str__ has called.")return self.__class__.__name__ + "__str__"

def __repr__(self):print("__repr__ has called.")return self.__class__.__name__ + "__repr__"a=A()

a#__repr__ has called.#A__repr__

print(a)#__str__ has called.#A__str__

str(a)#__str__ has called.#'A__str__'

序号目的所编写代码Python 实际调用

1

初始化一个实例

x = MyClass()

2

字符串的“官方”表现形式

repr(x)

3

字符串的“非正式”值

x.__str__()

4

字节数组的“非正式”值

bytes(x)

x.__bytes__()

5

格式化字符串的值

format(x, format_spec)

通常__str__()和__repr__()代码都是一样的,所以会这么写:

classStudent(object):def __init__(self, name):

self.name=namedef __str__(self):return 'Student object (name=%s)' %self.name__repr__ = __str__

六、跟迭代器有关的

序号目的所编写代码Python 实际调用

1

遍历某个序列

iter(seq)

2

从迭代器中获取下一个值

next(seq)

3

按逆序创建一个迭代器

reversed(seq)

iter内置函数对应的特殊方法是__iter_,它把一个序列变成迭代器。

classIter:def __init__(self, lis):

self.iterate=lisdef __iter__(self):for v inself.iterate:print("__iter__ has called.")yieldv

a= Iter(list(range(5)))

b=iter(a)print(next(b))print(next(b))"""__iter__ has called.

0

__iter__ has called.

1"""

next内置函数对应的特殊方法是__iter_,对迭代器进行迭代(惰性行)。

classIter:

count=0def __init__(self, lis):

self.iterate=lisdef __next__(self):print("__next__ has called.")try:

value=self.iterate[Iter.count]

Iter.count+= 1

exceptStopIteration:

exit()returnvalue

a= Iter(list(range(5)))print(next(a))print(next(a))"""__next__ has called.

0

__next__ has called.

1"""

至此我们已了解了绝大部分特殊方法,在python中,类对象的顶层是type类。下面是object类和type类的源码。它们的源码是底层实现的。

classobject:"""The most base type"""

def __delattr__(self, *args, **kwargs): #real signature unknown

"""Implement delattr(self, name)."""

pass

def __dir__(self): #real signature unknown; restored from __doc__

"""__dir__() -> list

default dir() implementation"""

return[]def __eq__(self, *args, **kwargs): #real signature unknown

"""Return self==value."""

pass

def __format__(self, *args, **kwargs): #real signature unknown

"""default object formatter"""

pass

def __getattribute__(self, *args, **kwargs): #real signature unknown

"""Return getattr(self, name)."""

pass

def __ge__(self, *args, **kwargs): #real signature unknown

"""Return self>=value."""

pass

def __gt__(self, *args, **kwargs): #real signature unknown

"""Return self>value."""

pass

def __hash__(self, *args, **kwargs): #real signature unknown

"""Return hash(self)."""

pass

def __init_subclass__(self, *args, **kwargs): #real signature unknown

"""This method is called when a class is subclassed.

The default implementation does nothing. It may be

overridden to extend subclasses."""

pass

def __init__(self): #known special case of object.__init__

"""Initialize self. See help(type(self)) for accurate signature."""

pass

def __le__(self, *args, **kwargs): #real signature unknown

"""Return self<=value."""

pass

def __lt__(self, *args, **kwargs): #real signature unknown

"""Return self

pass@staticmethod#known case of __new__

def __new__(cls, *more): #known special case of object.__new__

"""Create and return a new object. See help(type) for accurate signature."""

pass

def __ne__(self, *args, **kwargs): #real signature unknown

"""Return self!=value."""

pass

def __reduce_ex__(self, *args, **kwargs): #real signature unknown

"""helper for pickle"""

pass

def __reduce__(self, *args, **kwargs): #real signature unknown

"""helper for pickle"""

pass

def __repr__(self, *args, **kwargs): #real signature unknown

"""Return repr(self)."""

pass

def __setattr__(self, *args, **kwargs): #real signature unknown

"""Implement setattr(self, name, value)."""

pass

def __sizeof__(self): #real signature unknown; restored from __doc__

"""__sizeof__() -> int

size of object in memory, in bytes"""

return0def __str__(self, *args, **kwargs): #real signature unknown

"""Return str(self)."""

pass@classmethod#known case

def __subclasshook__(cls, subclass): #known special case of object.__subclasshook__

"""Abstract classes can override this to customize issubclass().

This is invoked early on by abc.ABCMeta.__subclasscheck__().

It should return True, False or NotImplemented. If it returns

NotImplemented, the normal algorithm is used. Otherwise, it

overrides the normal algorithm (and the outcome is cached)."""

pass

__class__ = None #(!) forward: type, real value is ''

__dict__ ={}__doc__ = ''

__module__ = ''

object

classtype(object):"""type(object_or_name, bases, dict)

type(object) -> the object's type

type(name, bases, dict) -> a new type"""

def mro(self): #real signature unknown; restored from __doc__

"""mro() -> list

return a type's method resolution order"""

return[]def __call__(self, *args, **kwargs): #real signature unknown

"""Call self as a function."""

pass

def __delattr__(self, *args, **kwargs): #real signature unknown

"""Implement delattr(self, name)."""

pass

def __dir__(self): #real signature unknown; restored from __doc__

"""__dir__() -> list

specialized __dir__ implementation for types"""

return[]def __getattribute__(self, *args, **kwargs): #real signature unknown

"""Return getattr(self, name)."""

pass

def __init__(cls, what, bases=None, dict=None): #known special case of type.__init__

"""type(object_or_name, bases, dict)

type(object) -> the object's type

type(name, bases, dict) -> a new type

# (copied from class doc)"""

pass

def __instancecheck__(self): #real signature unknown; restored from __doc__

"""__instancecheck__() -> bool

check if an object is an instance"""

returnFalse

@staticmethod#known case of __new__

def __new__(*args, **kwargs): #real signature unknown

"""Create and return a new object. See help(type) for accurate signature."""

pass

def __prepare__(self): #real signature unknown; restored from __doc__

"""__prepare__() -> dict

used to create the namespace for the class statement"""

return{}def __repr__(self, *args, **kwargs): #real signature unknown

"""Return repr(self)."""

pass

def __setattr__(self, *args, **kwargs): #real signature unknown

"""Implement setattr(self, name, value)."""

pass

def __sizeof__(self): #real signature unknown; restored from __doc__

"""__sizeof__() -> int

return memory consumption of the type object"""

return0def __subclasscheck__(self): #real signature unknown; restored from __doc__

"""__subclasscheck__() -> bool

check if a class is a subclass"""

returnFalsedef __subclasses__(self): #real signature unknown; restored from __doc__

"""__subclasses__() -> list of immediate subclasses"""

return[]__abstractmethods__ = property(lambda self: object(), lambda self, v: None, lambda self: None) #default

__bases__ =(

object,

)__base__ =object__basicsize__ = 864

__dictoffset__ = 264

__dict__ = None #(!) real value is ''

__flags__ = 2148291584

__itemsize__ = 40

__mro__ =(

None,#(!) forward: type, real value is ''

object,

)__name__ = 'type'

__qualname__ = 'type'

__text_signature__ =None__weakrefoffset__ = 368

type

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值