The decision of whether to use a method is not up to the API user, it is
up to the API designer, who should have a good idea of the range of
possible implementations.
You are concerned about the users being able to âquickly determine if
parentheses should be added or notâ, but I think you are making a
mountain out of a mole-hill:
-
If the user is familiar with the API, they will know from experience.
-
If they are not familiar, they will have to look it up anyway, to find
out what API they need to call. Is it str.upper or .uppercase or Upper
or convert_to_UPPER or something else? That will tell them whether it is
a method that needs parentheses or not.
-
If they are using an IDE or editor with code auto-completion, the
editor will add the parentheses for them.
-
Getting help is only a few key strokes away if you have a Python
interactive iterpreter open, which you should have:
ââ.upper
<built-in method upper of str object at 0x7f8cbea663b0>
Now I know I need parentheses.
Everything is hard if you are not familiar with the API:
Is is widget.mogrify(a, b)
or mogrified(widget, a, b)
or
widget.mogrified(b, a)
or something else? Worrying especially about
the need for parentheses or not is unnecessary.
You say:
âmake all methods that have zero arguments and will never have arguments
look like an attribute (without parentheses)â
Things that look like attributes imply that:
(1) They are cheap. I donât need to save the result for re-use.
for paragraph in document:
para = format(paragraph, page.width)
Thatâs great, unless calculating the page width is slow, then your whole
program is slow.
(2) It also implies that I can set the attribute:
page.width = 500
If that was written page.width()
I would know not even to try.
(3) Attributes should be things, not actions. A zero-argument method
is still an action, and should have parentheses:
# Yes, methods are actions.
dog.bark()
engine.start()
# No, looking up an attribute should not have side-effects.
# Actions should be methods, not attributes.
dog.bark
engine.start
You say:
âmake everything that returns a value have parentheses.â
Thatâs were we get Java getters and setters. They are horrible:
# Python
print(sys.ps1)
sys.ps1 = '>>> '
# Java-style getters and setters
print(sys.get_ps1())
sys.set_ps1('>>> ')
âOf these I currently tend towards the first solution: why would we
actually need parentheses anyways?â
Because referring to a function or method is not the same as calling
that function or method.
If callables automatically got called without parentheses, you couldnât
write this:
isinstance(value, float)
because the reference to float would automatically call it and give you
0.0, and you canât call isinstance(value, 0.0).
You couldnât pass functions or methods as key functions:
sorted(strings, str.upper)
# would automatically call str.upper and raise an exception
or as callback functions. Dependency injection would be horrible:
# Easy in Python
def myfunction(arg, randomness=random.random):
pass
value = myfunction(20) # Use the default source of randomness.
value = myfunction(20, myrandomness) # Inject the dependency.
Functional programming techniques would be horrible:
map(func, values) # would automatically call func and fail
It would make testing harder:
# This would fail:
for func in [str.lower, str.upper, str.index, str.find]:
test(func, value, expected)
Methods and functions are objects. Grabbing a reference to them is not
the same as calling them.
Ruby allows you to leave out parentheses when calling zero argument
functions, and it is a real pain. It means that things which are
trivially easy in Python are hard and annoying in Ruby.