python 类的特殊方法_Python 类特殊方法

object 是一个基类,或称之为元类。在 python2.x 上,不继承 object 类的称之为经典类,继承了 object 类的称之为新式类。

在 python3 种默认都是新式类,也即是所有的自定义类、基类都会继承object类。

object 类里面内置了许多特殊方法,这些方法的开头和结尾都是双下划线。

1. __dir__:返回一个列表,其中包含所有的属性和方法名(包含特殊方法)。函数原型如下:

def __dir__(self, *args, **kwargs):

""" Default dir() implementation. """

pass

下面举一个例子:

class Test():

pass

print(Test().__dir__()) # 也可执行 dir(obj) 来获取所有属性和方法

"""

输出如下:

['__module__', '__dict__', '__weakref__', '__doc__', '__repr__',

'__hash__', '__str__', '__getattribute__', '__setattr__', '__delattr__',

'__lt__', '__le__', '__eq__', '__ne__', '__gt__',

'__ge__', '__init__', '__new__', '__reduce_ex__', '__reduce__',

'__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__']

"""

2. __setattr__:当我们执行obj.属性名=属性值或setattr(obj,属性名,属性值),即为属性赋值时被调用。

class Foo(object):

def __init__(self):

self.name = 'Alex' # 打印 call __setattr__

# 重写__setattr__,为了使调用时打印输出,obj.xxx = value时调用

def __setattr__(self, key, value):

print('call __setattr__')

return object.__setattr__(self, key, value) # 调用基类的方法来赋值

f = Foo()

f.name = 'Jone' # 打印 call __setattr__

3. __getattribute__和__getattr__:使用类的对象进行obj.属性名或getattr(obj,属性名)来取对象属性的值的时候被调用,不管属性是否存在,

__getattribute__方法都会被调用。如果属性存在,则返回该属性的值,如果属性不存在,则返回None。我们在使用hasattr(obj,属性名)来判断

某个属性是否存在时,__getattribute__方法也会被调用。

与__getattr__的区别:

__getattribute__方法不管属性是否存在,都会被调用。而__getattr__只在属性不存在时调用,默认会抛出:

'AttributeError: xxx object has no attribute yyy'这样的错误,但我们可以对其进行重写,做我们需要的操作。

class Test(object):

def __init__(self):

self.name = 'Alex'

def __getattribute__(self, item):

print("call __getattribute__")

return object.__getattribute__(self, item)

def __getattr__(self, item):

print('call __getattr__')

print("%s不存在,但我可以返回一个值" % item)

return 54

f = Test()

print(f.name) # name 属性存在,打印 call __getattribute__

print(f.age) # age 属性不存在,打印 call __getattribute__,会返回None,

# 然后打印 call __getattr__, 但 __getattr__ 方法返回了54(如果没有被重写,则直接报异常),所以这里打印54。

4. __str__和__repr__:默认打印的是对象的内存地址等信息。

1)只重写__str__,则只能用于定义print(obj)时打印的内容,交互式命令行输出obj还是函数地址。

>>> class Foo(object):

... def __str__(self):

... return "我是Foo"

...

>>> f1 = Foo()

>>> print(f1)

我是Foo

>>> f1

2)只重写__repr__,则用于同时定义python命令行输出obj的内容,以及print(obj)的打印内容。

>>> class Foo(object):

... def __repr__(self):

... return "我是Foo"

...

>>> f1 = Foo()

>>> print(f1)

我是Foo

>>> f1

我是Foo

3)如果__str__和__repr__都被重写了,则__str__负责print的信息,而__repr__负责命令行直接输出obj的信息。

>>> class Foo():

... def __str__(self):

... return "我是Foo---str"

... def __repr__(self):

... return "我是Foo---repr"

...

>>> f1 = Foo()

>>> print(f1)

我是Foo---str

>>> f1

我是Foo---repr

5. __new__:__new__方法是一个静态方法,在调用时,传入你需要实例化的类名以及初始化参数列表,创建一个cls类的对象,并返回。其函数原型如下:

@staticmethod

def __new__(cls, *more):

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

pass

注意以下几点:

1)__new__在object被指定为@staticmethod,但更像是一个@classmethod,第一个参数传入类本身cls。

2)__new__在__init__之前运行,为传入的类(Foo)生成一个实例并返回。

3)__init__在__new__之后执行,为__new__返回的类实例进行初始化。

4)__init__是一个实例方法,是由实例来调用的。所以要执行__init__方法,必须先要由__new__生产一个实例。这就是为什么先执行__new__方法的原因。

class Foo(object):

# 后于__new__方法执行,为__new__方法生成的对象进行初始化

def __init__(self, name, age): # __new__返回的对象作为self传入__init__

print("执行__init__方法")

self.name = name

self.age = age

# __new__方法先于__init__方法执行,用于生成一个指定类的对象

def __new__(cls, *args, **kwargs): # 接收参数cls为Foo类,然后是f1 = Foo("Alex",age=32)里的name和age

print("执行__new__方法", args, kwargs)

ret = object.__new__(cls) # 调用__new__生成一个Foo对象

print(ret) # 打印 <__main__.foo object at>

return ret # 返回生成的 Foo 对象

f1 = Foo("Alex",age=32)

"""

output:

执行__new__方法 ('Alex',) {'age': 32}

执行__init__方法

"""

6. __gt__、__lt__、__ge__、__le__:这几个都是用于比较大小的,我们可以对其进行重写,来自定义对象如何比较大小。它们的原型如下:

# 判断是否大于等于 greater than or equal,在obj >= other时调用

def __ge__(self, *args, **kwargs):

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

pass

# 判断是否大于 greater than,在obj > other时调用

def __gt__(self, *args, **kwargs):

""" Return self > value. """

pass

# 判断是否小于等于 less than or equal,在obj <= other时调用

def __le__(self, *args, **kwargs):

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

pass

# 判断是否小于 less than,在obj

def __lt__(self, *args, **kwargs):

""" Return self < value. """

pass

下面举一个简单的例子:

class Car():

def __init__(self, price):

self.price = price

def __lt__(self, other):

print("execute __lt__")

return self.price < other.price

c1 = Car(100)

c2 = Car(200)

if c1 < c2:

print("car1 is cheaper.")

else:

print("car2 is cheaper.")

7. __eq__和__hash__:__hash__实际上是返回一个int值(计算类中所有成员的hash值),用来唯一标记这个对象。户自定义类中,如果你没有实

现__eq__和__hash__函数,那么class会继承到默认的__eq__和__hash__函数。

先来解释下什么是可hash对象:如果一个对象是可哈希的,那么在它的生存期内必须不可变(需要一个哈希函数__hash__),而且可以和其他对象比

较(需要比较方法__eq__).相同的对象一定有相同的哈希值。

1)不可hash:list, set, dict

2)可hash:数值,字符串,boolean

3)自定义类对象可不可hash:当类中只含可hash的成员时,该类定义的对象是可hash的。当我们实现的类想成为不可hash的类,则可以重写__eq__方法,

然后不重写__hash__,__hash__方法会被置None,该类的对象就不可hash了。如果不重写__eq__,则会使用基类的实现,比较对象时会比较它们的hash值。

class Foo(object):

def __init__(self):

self.name = 'Alex'

def __eq__(self, other):

return self.name == other.name

f1 = Foo()

f2 = Foo()

print(f1 == f2) # True

print(f1 is f2) # False

print(hash(f1) == hash(f2)) # 抛出异常TypeError错误,因为此时__hash__=None

8. __dict__属性:在 Python类的内部,无论是类属性还是实例属性,都是以字典的形式进行存储的,其中属性名作为键,而值作为该键对应的值。

为了方便用户查看类中包含哪些属性,Python 类提供了 __dict__ 属性。该属性可以用类名或者类的实例对象来调用。

1)每个类有自己的__dict__属性,就算存着继承关系,父类的__dict__ 并不会影响子类的__dict__,两者不共用。子类有自己的__dict__, 父类也有自

己的__dict__,子类的类属性、静态方法、普通方法、内置属性等放在子类的dict中,父类的放在父类dict中。

2)实例对象也有自己的__dict__属性, 存储 self.xxx 信息(只有属性,不包含方法),与1)不同的是父子类对象公用__dict__。

3)内置的数据类型没有__dict__属性。

我们在查找一个类属性或者对象属性的时候,也是通过__dict__字典来查的,查找顺序如下:

1)首先查找实例 obj.__dict__。

2)如果1)中未找到,则去查找类 type(obj).__dict__。

3)如果2)也未找到,则去查找父类的 __dict__,查找顺序也是先实例后类。

class Test(object):

x = 1 # 类属性

def __init__(self, y):

self.y = y # 实例属性

def fun(self):

print(self.y)

t = Test(2)

print(t.__dict__)

"""

通过实例调用__dict__,输出如下:

{'y': 2}

"""

print(Test.__dict__)

"""

因为__dict__不共用,所以输出内容不会涉及到基类object

{

'__module__': '__main__',

'x': 1,

'__init__': ,

'fun': ,

'__dict__': ,

'__weakref__': ,

'__doc__': None

}

"""

print(vars(t)) # __dict__ 也可以用 vars 函t数替代,功能完全相同

t.fun() # 在 type(t).__dict__ 中寻找,找到了直接返回

print(t.y) # 在 t.__dict__ 中寻找,找到了 y 直接返回

print(t.__dict__['y']) # 上面的调用机制实际上是这样的

print(t.x) # 在t.__dict__中找不到x,于是到type(t).__dict__中找到了x,并返回其值

print(type(t).__dict__['x']) # 上面的调用机制实际上是这样的

9. __getitem__:这个方法在 object 类里面没有被定义,因为一旦定义了这个方法,那么所有对该类对象的索引运算都会被拦截。

凡是在类中定义了这个 __getitem__ 方法,那么它的实例对象(假定为 obj),可以像这样 obj[index] 取值,当实例对象做

obj[index] 操作时,会调用类中的方法__getitem__ 方法。

class DataBase:

def __init__(self, id, address):

self.id = id

self.address = address

self.d = {

self.id: 1,

self.address: "192.168.1.1"

}

def __getitem__(self, key):

return self.d.get(key, "default")

data = DataBase(1, "192.168.2.11")

print(data["hi"])

print(data[data.id])

"""

default

1

"""

10. __len__:如果一个类表现得像一个 list,要获得有多少个元素,就得用 len();要让 len() 函数正常工作,类必须提供一个特

殊的方法__len__(),才能返回元素的个数。

class Fib:

def __init__(self, num):

a, b, L = 0, 1, []

for i in range(num):

L.append(a)

a, b = b, a+ b

self.numbers = L

def __str__(self):

return str(self.numbers)

def __len__(self):

return len(self.numbers)

f = Fib(10)

print(f)

print(len(f))

"""

[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

10

"""

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python中,`__getitem__`是一个特殊方法,用于定义对象的索引和切片行为。当您尝试通过索引或切片访问对象时,Python将调用此方法。这通常在您正在处理如列表(list)、字典(dict)、字符串(str)等可迭代对象时使用。 当继承自内置的序列型(例如list、str、tuple等)并使用`__getitem__`方法时,可以在的实例中实现更复杂的数据访问操作。 这是一个基本的示例,展示如何使用`__getitem__`方法来处理一个继承自list的自定义列表: ```python class MyList(list): def __getitem__(self, index): # 可以在这里实现一些特定的索引逻辑 print("Getting item at index", index) return super().__getitem__(index) my_list = MyList([1, 2, 3, 4]) print(my_list) # 输出: "Getting item at index 1" # 输出: 2 ``` 在这个例子中,当我们通过索引访问`my_list`中的元素时,Python会首先调用`__getitem__`方法,然后才调用内置的`list.__getitem__`方法。这意味着我们可以在这个方法中实现一些特定的逻辑,比如打印出每次访问的索引,或者返回特殊的结果。 需要注意的是,`__getitem__`方法通常返回被索引的对象,而不是该对象本身。这是因为Python的索引操作通常期望返回的是原始对象,而不是对同一对象的引用。例如,当我们使用索引访问列表时,我们期望得到的是列表中的元素,而不是对列表的引用。 此外,如果你想要实现切片操作,你可以在`__getitem__`方法中处理这些情况。例如,你可以定义如何根据起始和结束索引来生成一个新的序列。例如: ```python class MyList(list): def __getitem__(self, index): if isinstance(index, slice): start, stop = index.start, index.stop return MyList(super().__getitem__(start, stop)) else: return super().__getitem__(index) ``` 在这个例子中,如果尝试使用切片操作访问列表,`__getitem__`方法将返回一个新的`MyList`实例,其中包含了原始列表的子集。这对于处理似列表的数据结构非常有用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值