面向对象的 Python - 高级功能
在本文中,我们将研究 Python 提供的一些高级功能
类设计中的核心语法
在本文中,我们将研究 Python 如何让我们利用类中的运算符。Python 主要是对象和对对象的方法调用,即使它被一些方便的语法隐藏,这种情况也会继续。
>>> 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__ 添加到我们自己的类中,我们也可以这样做吗?让我们尝试这样做。
我们有一个名为 Sumlist 的类,它有一个构造函数 __init__,它将列表作为名为 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])
输出
[103, 206, 309, 412, 515]
但是有许多方法由其他魔法方法内部管理。下面是其中一些,
'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__()
从内置类型继承
类也可以从内置类型继承,这意味着从任何内置类型继承并利用那里的所有功能。
在下面的例子中,我们从字典继承,但随后我们正在实现它的一个方法 __setitem__。当我们在字典中设置键和值时,将调用此方法 (setitem)。由于这是一个魔术方法,因此将隐式调用该方法。
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]))
输出
setting a key and value! setting a key and value! a = 10 b = 20
让我们扩展前面的例子,下面我们调用了两个魔术方法,分别称为 __getitem__ 和 __setitem__,在处理列表索引时更好地调用它们。
# Mylist 继承自"list"对象,但索引从 1 开始,而不是 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 # 我们用下标访问一个值,比如 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!
输出
['a', 'b', 'c'] a HELLO
在上面的例子中,我们在 Mylist 中设置了一个三项列表,并隐式调用了 __init__ 方法,当我们打印元素 x 时,我们得到了三项列表 (['a','b','c'])。然后我们将另一个元素附加到此列表。稍后我们请求索引 1 和索引 4。但是如果您看到输出,我们将从 (index-1) 获取我们要求的元素。我们知道列表索引从 0 开始,但这里索引从 1 开始(这就是我们获取列表的第一项的原因)。
命名约定
在此,我们将研究变量的名称,尤其是私有变量,以及全球 Python 程序员使用的约定。虽然变量被指定为私有变量,但 Python 中没有隐私,这是设计使然。与任何其他有据可查的语言一样,Python 有命名和样式约定,它虽然不强制执行,但还是会加以推广。 Python 的创始人"Guido van Rossum"编写了一本样式指南,其中描述了最佳实践和名称的使用,称为 PEP8。这是此链接, https://www.python.org/dev/peps/pep-0008/
PEP 代表 Python 增强提案,是一系列在 Python 社区中分发的文档,用于讨论拟议的更改。例如,建议所有人,
- 模块名称 − all_lower_case
- 类名称和异常名称 − CamelCase
- 全局和本地名称 − all_lower_case
- 函数和方法名称 − all_lower_case
- 常量 − ALL_UPPER_CASE
这些只是建议,您可以根据需要进行更改。但是由于大多数开发人员都遵循这些建议,因此您的代码的可读性可能会降低。
为什么要遵守惯例?
我们可以遵循 PEP 建议,因为它允许我们获得,
- 对绝大多数开发人员来说更熟悉
- 对您的代码的大多数读者来说更清晰。
- 将与在同一代码库上工作的其他贡献者的风格相匹配。
- 专业软件开发人员的标志
- 每个人都会接受你。
变量命名 −"public(公共)"和"Private(私有)"
在 Python 中,当我们处理模块和类时,我们将一些变量或属性指定为私有。在 Python 中,不存在"Private(私有)"实例变量,除非在对象内部,否则无法访问。私有只是意味着它们不打算由代码用户使用,而是打算在内部使用。一般来说,大多数 Python 开发人员都会遵循一个惯例,即以下划线为前缀的名称。_attrval(下面的示例)应被视为 API 或任何 Python 代码的非公共部分,无论它是函数、方法还是数据成员。以下是我们遵循的命名约定,
公共属性或变量(旨在由该模块的导入者或此类的用户使用)−regular_lower_case
私有属性或变量(模块或类内部使用)−_single_leading_underscore
不应被子类化的私有属性−__double_leading_underscore
魔法属性−__double_underscores__(使用它们,而不是创建它们)
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)
输出
setting the "var" attribute 10 no privacy!