Operator overloading is mostly useful when you're making a new class that falls into an existing "Abstract Base Class" (ABC) -- indeed, many of the ABCs in standard library module collections rely on the presence of certain special methods (and special methods, one with names starting and ending with double underscores AKA "dunders", are exactly the way you perform operator overloading in Python). This provides good starting guidance.
For example, a Container class must override special method __contains__, i.e., the membership check operator item in container (as in, if item in container: -- don't confuse with the for statement, for item in container:, which relies on __iter__!-). Similarly, a Hashable must override __hash__, a Sized must override __len__, a Sequence or a Mapping must override __getitem__, and so forth. (Moreover, the ABCs can provide your class with mixin functionality -- e.g., both Sequence and Mapping can provide __contains__ on the basis of your supplied __getitem__ override, and thereby automatically make your class a Container).
Beyond the collections, you'll want to override special methods (i.e. provide for operator overloading) mostly if your new class "is a number". Other special cases exist, but resist the temptation of overloading operators "just for coolness", with no semantic connection to the "normal" meanings, as C++'s streams do for << and >> and Python strings (in Python 2.*, fortunately not in 3.* any more;-) do for % -- when such operators do not any more mean "bit-shifting" or "division remainder", you're just engendering confusion. A language's standard library can get away with it (though it shouldn't;-), but unless your library gets as widespread as the language's standard one, the confusion will hurt!-)
编辑:我想添加一个关于重载 == 的解释性注释,因为似乎各种评论者都误解了这一点,这让我很困惑。是的,is 存在,但它是不同的操作。假设我有一个对象 x,它要么来自我的自定义类,要么是一个整数。我想看看x是否是数字500。但是如果你设置x = 500,然后测试x是500,你会得到False,由于 Python 缓存数字的方式所致。如果使用 50,它将返回 True。但您不能使用 is,因为如果 x is,您可能希望 x == 500 返回 True你的班级的一个实例。令人困惑?确实。但这是您需要了解才能成功重载运算符的细节。
I've written software with significant amounts of overloading, and lately I regret that policy. I would say this:
Only overload operators if it's the natural, expected thing to do and doesn't have any side effects.
So if you make a new RomanNumeral class, it makes sense to overload addition and subtraction etc. But don't overload it unless it's natural: it makes no sense to define addition and subtraction for a Car or a Vehicle object.
Another rule of thumb: don't overload ==. It makes it very hard (though not impossible) to actually test if two objects are the same. I made this mistake and paid for it for a long time.
As for when to overload +=, ++ etc, I'd actually say: only overload additional operators if you have a lot of demand for that functionality. It's easier to have one way to do something than five. Sure, it means sometimes you'll have to write x = x + 1 instead of x += 1, but more code is ok if it's clearer.
In general, like with many 'fancy' features, it's easy to think that you want something when you don't really, implement a bunch of stuff, not notice the side effects, and then figure it out later. Err on the conservative side.
EDIT: I wanted to add an explanatory note about overloading ==, because it seems various commenters misunderstand this, and it's caught me out. Yes, is exists, but it's a different operation. Say I have an object x, which is either from my custom class, or is an integer. I want to see if x is the number 500. But if you set x = 500, then later test x is 500, you will get False, due to the way Python caches numbers. With 50, it would return True. But you can't use is, because you might want x == 500 to return True if x is an instance of your class. Confusing? Definitely. But this is the kind of detail you need to understand to successfully overload operators.
class pipely(object):
def __init__(self, *args, **kw):
self._args = args
self.__dict__.update(kw)
def __ror__(self, other):
return ( self.map(x) for x in other if self.filter(x) )
def map(self, x):
return x
def filter(self, x):
return True
class sieve(pipely):
def filter(self, x):
n = self._args[0]
return x==n or x%n
class strify(pipely):
def map(self, x):
return str(x)
class startswith(pipely):
def filter(self, x):
n=str(self._args[0])
if x.startswith(n):
return x
print"*"*80
for i in xrange(2,100) | sieve(2) | sieve(3) | sieve(5) | sieve(7) | strify() | startswith(5):
print i
print"*"*80
for i in xrange(2,100) | sieve(2) | sieve(3) | sieve(5) | sieve(7) | pipely(map=str) | startswith(5):
print i
print"*"*80
for i in xrange(2,100) | sieve(2) | sieve(3) | sieve(5) | sieve(7) | pipely(map=str) | pipely(filter=lambda x: x.startswith('5')):
print i
Here is an example that uses the bitwise or operation to simulate a unix pipeline. This is intended as a counter example to most of the rules of thumb.
I just found Lumberjack which uses this syntax in real code
class pipely(object):
def __init__(self, *args, **kw):
self._args = args
self.__dict__.update(kw)
def __ror__(self, other):
return ( self.map(x) for x in other if self.filter(x) )
def map(self, x):
return x
def filter(self, x):
return True
class sieve(pipely):
def filter(self, x):
n = self._args[0]
return x==n or x%n
class strify(pipely):
def map(self, x):
return str(x)
class startswith(pipely):
def filter(self, x):
n=str(self._args[0])
if x.startswith(n):
return x
print"*"*80
for i in xrange(2,100) | sieve(2) | sieve(3) | sieve(5) | sieve(7) | strify() | startswith(5):
print i
print"*"*80
for i in xrange(2,100) | sieve(2) | sieve(3) | sieve(5) | sieve(7) | pipely(map=str) | startswith(5):
print i
print"*"*80
for i in xrange(2,100) | sieve(2) | sieve(3) | sieve(5) | sieve(7) | pipely(map=str) | pipely(filter=lambda x: x.startswith('5')):
print i
Python's overloading is "safer" in general than C++'s -- for example, the assignment operator can't be overloaded, and += has a sensible default implementation.
In some ways, though, overloading in Python is still as "broken" as in C++. Programmers should restrain the desire to "re-use" an operator for unrelated purposes, such as C++ re-using the bitshifts to perform string formatting and parsing. Don't overload an operator with different semantics from your implementation just to get prettier syntax.
Modern Python style strongly discourages "rogue" overloading, but many aspects of the language and standard library retain poorly-named operators for backwards compatibility. For example:
%: modulus and string formatting
+: addition and sequence concatenation
*: multiplication and sequence repetition
So, rule of thumb? If your operator implementation will surprise people, don't do it.
发布评论
评论(4)
当您创建属于现有“抽象基类”(ABC) 的新类时,运算符重载非常有用——事实上,标准库模块中的许多 ABC 集合 依赖于某些特殊方法的存在(以及特殊方法,一种名称以双下划线开头和结尾的方法,又称为“dunders” ,正是在 Python 中执行运算符重载的方式)。这提供了良好的启动指导。
例如,
Container
类必须重写特殊方法__contains__
,即成员资格检查运算符item in container
(如if item in container:
- 不要与for
语句混淆,for item in container:
,它依赖于 <代码>__iter__!-)。同样,
Hashable
必须覆盖__hash__
,Sized
必须覆盖__len__
,Sequence
> 或Mapping
必须覆盖__getitem__
,等等。 (此外,ABC 可以为您的类提供 mixin 功能 - 例如,Sequence
和Mapping
都可以根据您提供的提供__contains__
__getitem__
覆盖,从而自动使您的类成为Container
)。除了
集合
之外,如果您的新类“是一个数字”,您还需要重写特殊方法(即提供运算符重载)。还存在其他特殊情况,但要抵制“只是为了酷”而重载运算符的诱惑,与“正常”含义没有语义联系,就像 C++ 的流对<<
和> 所做的那样;>
和 Python 字符串(在 Python2.*
中,幸运的是不再在3.*
中;-)对% - 当此类运算符不再意味着“位移位”或“除法余数”时,您只会造成混乱。一种语言的标准库可以摆脱它(尽管它不应该;-),但是除非你的库像该语言的标准库一样广泛,否则这种混乱将会造成伤害!-)
Operator overloading is mostly useful when you're making a new class that falls into an existing "Abstract Base Class" (ABC) -- indeed, many of the ABCs in standard library module collections rely on the presence of certain special methods (and special methods, one with names starting and ending with double underscores AKA "dunders", are exactly the way you perform operator overloading in Python). This provides good starting guidance.
For example, a
Container
class must override special method__contains__
, i.e., the membership check operatoritem in container
(as in,if item in container:
-- don't confuse with thefor
statement,for item in container:
, which relies on__iter__
!-).Similarly, a
Hashable
must override__hash__
, aSized
must override__len__
, aSequence
or aMapping
must override__getitem__
, and so forth. (Moreover, the ABCs can provide your class with mixin functionality -- e.g., bothSequence
andMapping
can provide__contains__
on the basis of your supplied__getitem__
override, and thereby automatically make your class aContainer
).Beyond the
collections
, you'll want to override special methods (i.e. provide for operator overloading) mostly if your new class "is a number". Other special cases exist, but resist the temptation of overloading operators "just for coolness", with no semantic connection to the "normal" meanings, as C++'s streams do for<<
and>>
and Python strings (in Python2.*
, fortunately not in3.*
any more;-) do for%
-- when such operators do not any more mean "bit-shifting" or "division remainder", you're just engendering confusion. A language's standard library can get away with it (though it shouldn't;-), but unless your library gets as widespread as the language's standard one, the confusion will hurt!-)我编写过具有大量重载的软件,最近我对这一政策感到遗憾。我会这样说:
仅重载运算符,如果它是自然的、预期的事情并且没有任何副作用。
因此,如果您创建一个新的
RomanNumeral
类,它重载加法和减法等是有意义的。但除非很自然,否则不要重载它:为Car
或Vehicle
对象定义加法和减法是没有意义的。另一个经验法则:不要超载
==
。这使得实际测试两个对象是否相同变得非常困难(尽管并非不可能)。我犯了这个错误,并为此付出了很长一段时间的代价。至于何时重载
+=
、++
等,我实际上会说:只有当您对该功能有很多需求时才重载其他运算符。用一种方法做某事比用五种方法更容易。当然,这意味着有时您必须编写x = x + 1
而不是x += 1
,但如果更清晰的话,更多代码是可以的。一般来说,就像许多“花哨”的功能一样,很容易认为您想要某些东西,但实际上并不需要,实现一堆东西,没有注意到副作用,然后稍后再弄清楚。偏于保守。
编辑:我想添加一个关于重载
==
的解释性注释,因为似乎各种评论者都误解了这一点,这让我很困惑。是的,is
存在,但它是不同的操作。假设我有一个对象x
,它要么来自我的自定义类,要么是一个整数。我想看看x
是否是数字500。但是如果你设置x = 500
,然后测试x是500
,你会得到False
,由于 Python 缓存数字的方式所致。如果使用50
,它将返回True
。但您不能使用is
,因为如果x
is,您可能希望x == 500
返回True
你的班级的一个实例。令人困惑?确实。但这是您需要了解才能成功重载运算符的细节。I've written software with significant amounts of overloading, and lately I regret that policy. I would say this:
Only overload operators if it's the natural, expected thing to do and doesn't have any side effects.
So if you make a new
RomanNumeral
class, it makes sense to overload addition and subtraction etc. But don't overload it unless it's natural: it makes no sense to define addition and subtraction for aCar
or aVehicle
object.Another rule of thumb: don't overload
==
. It makes it very hard (though not impossible) to actually test if two objects are the same. I made this mistake and paid for it for a long time.As for when to overload
+=
,++
etc, I'd actually say: only overload additional operators if you have a lot of demand for that functionality. It's easier to have one way to do something than five. Sure, it means sometimes you'll have to writex = x + 1
instead ofx += 1
, but more code is ok if it's clearer.In general, like with many 'fancy' features, it's easy to think that you want something when you don't really, implement a bunch of stuff, not notice the side effects, and then figure it out later. Err on the conservative side.
EDIT: I wanted to add an explanatory note about overloading
==
, because it seems various commenters misunderstand this, and it's caught me out. Yes,is
exists, but it's a different operation. Say I have an objectx
, which is either from my custom class, or is an integer. I want to see ifx
is the number 500. But if you setx = 500
, then later testx is 500
, you will getFalse
, due to the way Python caches numbers. With50
, it would returnTrue
. But you can't useis
, because you might wantx == 500
to returnTrue
ifx
is an instance of your class. Confusing? Definitely. But this is the kind of detail you need to understand to successfully overload operators.下面是一个使用按位或运算来模拟 UNIX 管道的示例。这是大多数经验法则的反例。
我刚刚发现 Lumberjack 在实际代码中使用此语法
Here is an example that uses the bitwise or operation to simulate a unix pipeline. This is intended as a counter example to most of the rules of thumb.
I just found Lumberjack which uses this syntax in real code
一般来说,Python 的重载比 C++ 的重载“更安全”——例如,赋值运算符不能重载,并且
+=
有一个合理的默认实现。但在某些方面,Python 中的重载仍然像 C++ 中一样“损坏”。程序员应该抑制出于不相关目的“重用”运算符的愿望,例如 C++ 重用位移位来执行字符串格式化和解析。不要仅仅为了获得更漂亮的语法而重载具有与您的实现不同的语义的运算符。
现代 Python 风格强烈反对“流氓”重载,但该语言和标准库的许多方面保留了命名不当的运算符以实现向后兼容性。例如:
%
:模数和字符串格式+
:加法和序列连接*
:乘法和序列重复那么,经验法则是什么?如果你的操作符实现会让人们感到惊讶,那就不要这样做。
Python's overloading is "safer" in general than C++'s -- for example, the assignment operator can't be overloaded, and
+=
has a sensible default implementation.In some ways, though, overloading in Python is still as "broken" as in C++. Programmers should restrain the desire to "re-use" an operator for unrelated purposes, such as C++ re-using the bitshifts to perform string formatting and parsing. Don't overload an operator with different semantics from your implementation just to get prettier syntax.
Modern Python style strongly discourages "rogue" overloading, but many aspects of the language and standard library retain poorly-named operators for backwards compatibility. For example:
%
: modulus and string formatting+
: addition and sequence concatenation*
: multiplication and sequence repetitionSo, rule of thumb? If your operator implementation will surprise people, don't do it.