Object Oriented Python 简明教程

Object Oriented Python - Advanced Features

在此,我们将研究 Python 提供的部分高级特性

In this we will look into some of the advanced features which Python provide

Core Syntax in our Class design

在此,我们将关注 Python 如何让我们利用我们的类中的操作符。Python 主要涉及对象和方法在对象上的调用,即使它隐藏在某种便捷的语法中时也是如此。

In this we will look onto, how Python allows us to take advantage of operators in our classes. Python is largely objects and methods call on objects and this even goes on even when its hidden by some convenient syntax.

>>> var1 = 'Hello'
>>> var2 = ' World!'
>>> var1 + var2
'Hello World!'
>>>
>>> var1.__add__(var2)
'Hello World!'
>>> num1 = 45
>>> num2 = 60
>>> num1.__add__(num2)
105
>>> var3 = ['a', 'b']
>>> var4 = ['hello', ' John']
>>> var3.__add__(var4)
['a', 'b', 'hello', ' John']

因此,如果我们需要将魔术方法 add 添加到我们自己的类,我们可以这样做吗。让我们尝试。

So if we have to add magic method add to our own classes, could we do that too. Let’s try to do that.

我们有一个名为 Sumlist 的类,它具有一个构造函数 init ,该构造函数获取一个列表作为名为 my_list 的参数。

We have a class called Sumlist which has a contructor init which takes list as an argument called my_list.

class SumList(object):
   def __init__(self, my_list):
      self.mylist = my_list
   def __add__(self, other):
     new_list = [ x + y for x, y in zip(self.mylist, other.mylist)]

     return SumList(new_list)

   def __repr__(self):
      return str(self.mylist)

aa = SumList([3,6, 9, 12, 15])

bb = SumList([100, 200, 300, 400, 500])
cc = aa + bb # aa.__add__(bb)
print(cc) # should gives us a list ([103, 206, 309, 412, 515])

Output

[103, 206, 309, 412, 515]

但有很多方法是由其他魔术方法内部管理的。以下列出部分方法,

But there are many methods which are internally managed by others magic methods. Below are some of them,

'abc' in var # var.__contains__('abc')
var == 'abc' # var.__eq__('abc')
var[1] # var.__getitem__(1)
var[1:3] # var.__getslice__(1, 3)
len(var) # var.__len__()
print(var) # var.__repr__()

Inheriting From built-in types

类还可从内置类型继承,这意味着从任何内置类型继承并利用在其中发现的所有功能。

Classes can also inherit from built-in types this means inherits from any built-in and take advantage of all the functionality found there.

在以下示例中,我们正在从字典继承,但我们正在实现它的一种方法 setitem 。在我们设置字典中的键值时,会调用此 (setitem) 方法。由于这是一个魔术方法,因此它将隐式调用。

In below example we are inheriting from dictionary but then we are implementing one of its method setitem. This (setitem) is invoked when we set key and value in the dictionary. As this is a magic method, this will be called implicitly.

class MyDict(dict):

   def __setitem__(self, key, val):
      print('setting a key and value!')
      dict.__setitem__(self, key, val)

dd = MyDict()
dd['a'] = 10
dd['b'] = 20

for key in dd.keys():
   print('{0} = {1}'.format(key, dd[key]))

Output

setting a key and value!
setting a key and value!
a = 10
b = 20

让我们扩展我们的前一个示例,以下我们调用了两个魔术方法 getitemsetitem ,在处理列表索引时会更好用。

Let’s extend our previous example, below we have called two magic methods called getitem and setitem better invoked when we deal with list index.

# Mylist inherits from 'list' object but indexes from 1 instead for 0!
class Mylist(list): # inherits from list
   def __getitem__(self, index):
      if index == 0:
         raise IndexError
      if index > 0:
         index = index - 1
         return list.__getitem__(self, index) # this method is called when

# we access a value with subscript like x[1]
   def __setitem__(self, index, value):
      if index == 0:
         raise IndexError
      if index > 0:
      index = index - 1
      list.__setitem__(self, index, value)

x = Mylist(['a', 'b', 'c']) # __init__() inherited from builtin list

print(x) # __repr__() inherited from builtin list

x.append('HELLO'); # append() inherited from builtin list

print(x[1]) # 'a' (Mylist.__getitem__ cutomizes list superclass
               # method. index is 1, but reflects 0!

print (x[4]) # 'HELLO' (index is 4 but reflects 3!

Output

['a', 'b', 'c']
a
HELLO

在上面的示例中,我们在 Mylist 中设置了一个三项列表,并隐式调用 init 方法,并且在我们打印元素 x 时,我们获得了三项列表 ([‘a’,’b’,’c’])。然后,我们将另一元素附加到该列表。稍后我们询问索引 1 和索引 4。但如果你查看输出,我们正在从 (index-1) 中获取我们所要求的元素。众所周知,列表索引从 0 开始,但这里的索引从 1 开始(这就是我们获取列表中第一个项的原因)。

In above example, we set a three item list in Mylist and implicitly init method is called and when we print the element x, we get the three item list ([‘a’,’b’,’c’]). Then we append another element to this list. Later we ask for index 1 and index 4. But if you see the output, we are getting element from the (index-1) what we have asked for. As we know list indexing start from 0 but here the indexing start from 1 (that’s why we are getting the first item of the list).

Naming Conventions

在此,我们将研究我们对变量(特别是私有变量)使用的名称以及全世界 Python 程序员使用的约定。尽管变量被指定为私有的,但 Python 中没有隐私,这是设计使然。与任何其他有据可查的语言一样,Python 也有命令和风格方面的惯例,虽然它并不强制执行它们,但会推广它们。有一个由 Guido van Rossum” the originator of Python, that describe the best practices and use of name and is called PEP8. Here is the link for this, https://www.python.org/dev/peps/pep-0008/ 编写的风格指南

In this we will look into names we’ll used for variables especially private variables and conventions used by Python programmers worldwide. Although variables are designated as private but there is not privacy in Python and this by design. Like any other well documented languages, Python has naming and style conventions that it promote although it doesn’t enforce them. There is a style guide written by “Guido van Rossum” the originator of Python, that describe the best practices and use of name and is called PEP8. Here is the link for this, https://www.python.org/dev/peps/pep-0008/

PEP 代表 Python 增强提案,是一系列文档,在 Python 社区中分发,以讨论提议的更改。例如,建议所有人,

PEP stands for Python enhancement proposal and is a series of documentation that distributed among the Python community to discuss proposed changes. For example it is recommended all,

  1. Module names − all_lower_case

  2. Class names and exception names − CamelCase

  3. Global and local names − all_lower_case

  4. Functions and method names − all_lower_case

  5. Constants − ALL_UPPER_CASE

这些只是建议,如果你愿意,可以随时更改。但由于大多数开发人员都遵循这些建议,因此你的代码的可读性可能会降低。

These are just the recommendation, you can vary if you like. But as most of the developers follows these recommendation so might me your code is less readable.

Why conform to convention?

我们可以遵循 PEP 建议,因为这允许我们获得,

We can follow the PEP recommendation we it allows us to get,

  1. More familiar to the vast majority of developers

  2. Clearer to most readers of your code.

  3. Will match style of other contributers who work on same code base.

  4. Mark of a professional software developers

  5. Everyone will accept you.

Variable Naming − ‘Public’ and ‘Private’

在 Python 中,当我们处理模块和类时,我们将一些变量或属性指定为私有。在 Python 中,不存在“私有”实例变量,除了对象内部以外,无法访问该变量。私有简单地意味着它们根本不是供代码用户使用,而是供内部使用。通常,大多数 Python 开发人员遵循一个惯例,即一个名称以下划线开头,例如。 _attrval(以下示例)应被视为 API 或任何 Python 代码的非公有部分,无论它是一个函数、一个方法还是一个数据成员。以下是我们遵循的命名约定,

In Python, when we are dealing with modules and classes, we designate some variables or attribute as private. In Python, there is no existence of “Private” instance variable which cannot be accessed except inside an object. Private simply means they are simply not intended to be used by the users of the code instead they are intended to be used internally. In general, a convention is being followed by most Python developers i.e. a name prefixed with an underscore for example. _attrval (example below) should be treated as a non-public part of the API or any Python code, whether it is a function, a method or a data member. Below is the naming convention we follow,

  1. Public attributes or variables (intended to be used by the importer of this module or user of this class) −regular_lower_case

  2. Private attributes or variables (internal use by the module or class) −_single_leading_underscore

  3. Private attributes that shouldn’t be subclassed −__double_leading_underscore

  4. Magic attributes −double_underscores(use them, don’t create them)

class GetSet(object):

   instance_count = 0 # public

   __mangled_name = 'no privacy!' # special variable

   def __init__(self, value):
      self._attrval = value # _attrval is for internal use only
      GetSet.instance_count += 1

   @property
   def var(self):
      print('Getting the "var" attribute')
      return self._attrval

   @var.setter
   def var(self, value):
      print('setting the "var" attribute')
      self._attrval = value

   @var.deleter
   def var(self):
      print('deleting the "var" attribute')
      self._attrval = None

cc = GetSet(5)
cc.var = 10 # public name
print(cc._attrval)
print(cc._GetSet__mangled_name)

Output

setting the "var" attribute
10
no privacy!