Home  >  Article  >  Backend Development  >  python magic method-detailed explanation of custom sequence

python magic method-detailed explanation of custom sequence

WBOY
WBOYOriginal
2016-08-04 08:55:411240browse

The related magic methods of custom sequences allow the classes we create to have the characteristics of sequences, allowing them to be used just like python’s built-in sequences (dict, tuple, list, string, etc.).

If you want to implement this function, you must follow the relevant protocols of python. The so-called agreement is some agreed content. For example, if you want to implement iteration in a class, you must implement two magic methods: __iter__ and next (__new__ in python3.x). __iter__ should return an object, which must implement the next method, which usually returns self itself. The next method must return the next element every time it is called, and trigger the StopIteration exception when the elements are exhausted.

In fact, the essence of the for loop is to first call the __iter__ method of the object, then repeatedly call the next method of the object returned by the __iter__ method, stop when the StopIteration exception is triggered, and handle the exception internally, so we cannot see it. Exception thrown.

This relationship is just like an interface. If you review the magic methods in the previous articles, you can find that the results obtained by many built-in functions are the return values ​​of the corresponding magic methods.

Here are the related magic methods:

•__len__(self)

•Returns the length of the container. Both mutable and immutable containers must implement it, it is part of the protocol.

•__getitem__(self, key)

•Define the behavior produced by using self[key] when an item is accessed. This is also part of the protocol for mutable and immutable containers. A TypeError will be raised if the key is of the wrong type; a KeyError will be raised if the key does not have a suitable value.

•__setitem__(self, key, value)

•Define the behavior produced by using self[key] = value when an item is assigned a value. This is also part of the mutable container protocol. Moreover, KeyError and TypeError will also be generated in corresponding situations.

•__delitem__(self, key)

•Define the behavior when an item is deleted. (e.g. del self[key]). This is part of the mutable container protocol. An appropriate exception must be thrown when you use an invalid key.

•__iter__(self)

•Returns a container iterator. In many cases, an iterator will be returned, especially when the built-in iter() method is called, and when the for x in container: method is used to loop. Iterators are objects themselves, and they must define an __iter__ method that returns self.

•__reversed__(self)

•Implement the behavior when reversed() is called. The reversed version of the sequence should be returned. Implement it only if the sequence is ordered, such as a list or tuple.

•__contains__(self, item)

•Defines the behavior that occurs when calling in and not in to test whether a member exists. This is not required by the protocol, but you can implement it according to your own requirements. When __contains__ is not defined, Python will iterate over the sequence and return True when the required value is found.

•__missing__(self, key)

•It is used in subclasses of dict. It defines the behavior that occurs when a key that does not exist in the dictionary is accessed. (For example, if I have a dictionary d, and d["george"] is used when "george" is not a key in the dictionary, d.__missing__("george") will be called).

Here is a code example:

class Foo(object):
  def __init__(self, key, value):
    self.key = []
    self.value = []
    self.key.append(key)
    self.value.append(value)

  def __len__(self):
    return len(self.key)

  def __getitem__(self, item):
    try:
      __index = self.key.index(item)
      return self.value[__index]
    except ValueError:
      raise KeyError('can not find the key')

  def __setitem__(self, key, value):
    if key not in self.key:
      self.key.append(key)
      self.value.append(value)
    else:
      __index = self.key.index(key)
      self.value[__index] = value

  def __delitem__(self, key):
    try:
      __index = self.key.index(key)
      del self.key[__index]
      del self.value[__index]
    except ValueError:
      raise KeyError('can not find the key')

  def __str__(self):
    result_list = []
    for index in xrange(len(self.key)):
      __key = self.key[index]
      __value = self.value[index]
      result = __key, __value
      result_list.append(result)
    return str(result_list)

  def __iter__(self):
    self.__index = 0
    return self

  def next(self):
    if self.__index == len(self.key):
      self.__index = 0
      raise StopIteration()
    else:
      __key = self.key[self.__index]
      __value = self.value[self.__index]
      result = __key, __value
      self.__index += 1
      return result

  def __reversed__(self):
    __result = self.value[:]
    __result.reverse()
    return __result

  def __contains__(self, item):
    if item in self.value:
      return True
    else:
      return False

Here we create a class that simulates a dictionary. This class maintains two lists internally. Key is responsible for storing keys, and value is responsible for storing values. The two lists correspond one-to-one through indexes, thereby achieving the purpose of simulating a dictionary.

First, let’s take a look at the __len__ method. According to the protocol, this method should return the length of the container. Because this class is designed to require that the two lists must be of equal length, so in theory, the length of whichever list is returned will be the same. Here I choose to return the length of key.

Then there is the __getitem__ method. This method will call a.__getitem__('scolia') when a['scolia']. In other words, this method defines the acquisition of elements. My idea here is to first find the index built in the key list, then use the index to find the corresponding element in the value list, and then return it. Then to further disguise it as a dictionary, I caught the ValueError that might be raised (which is the exception that is triggered when item is not in the key list) and disguised it as a KeyError when the dictionary cannot find the key.

Theoretically, as long as the above two methods are implemented, you can get an immutable container. But I felt unsatisfied so I continued to expand.

__setitem__(self, key, value)方法定义了 a['scolia'] = 'good' 这种操作时的行为,此时将会调用a.__setitem__('scolia', 'good') 因为是绑定方法,所以self是自动传递的,我们不用理。这里我也模拟了字典中对同一个键赋值时会造成覆盖的特性。这个方法不用返回任何值,所以return语句也省略了。

__delitem__(self, key)方法定义了del a['scolia'] 这类操作时候的行为,里面的‘scolia'就作为参数传进去。这里也进行了异常的转换。

只有实现里以上四个方法,就可以当做可变容器来使用了。有同学可能发现并没有切片对应的魔法方法,而事实上,我也暂时没有找到先,这部分内容先搁着一边。

接下来的 __str__ 是对应于 str() 函数,在类的表示中会继续讨论,这里是为了 print 语句好看才加进去的,因为print语句默认就是调用str()函数。

__iter__和next方法在开头的时候讨论过了,这里是为了能让其进行迭代操作而加入的。

__reversed__(self)方法返回一个倒序后的副本,这里体现了有序性,当然是否需要还是要看个人。

__contains__实现了成员判断,这里我们更关心value列表中的数据,所以判断的是value列表。该方法要求返回布尔值。

下面是相应的测试:

a = Foo('scolia', 'good')
a[123] = 321
a[456] = 654
a[789] = 987
print a
del a[789]
print a
for x, y in a:
  print x, y
print reversed(a)
print 123 in a
print 321 in a

•__missing__(self, key)

class Boo(dict):
  def __new__(cls, *args, **kwargs):
    return super(Boo, cls).__new__(cls)

  def __missing__(self, key):
    return 'The key(%s) can not be find.'% key

测试:

b = Boo()
b['scolia'] = 'good'
print b['scolia']
print b['123']

 

当然你也可以在找不到 key 的时候触发异常,具体实现看个人需求。

以上这篇python魔法方法-自定义序列详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持脚本之家。

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn