ひどいタイトルで申し訳ありませんが、うまくいけば、これらのスニペットがあなたに要点を与えるでしょう。
方法1:
class Person:
def __init__(self, name):
self.name = name
def set_name(self, new_name):
self.name = ' '.join(s[0].upper() + s[1:] for s in new_name.split(' '))
def __main__():
me = Person('Foo')
me.set_name('foo bar')
方法2:
class Person:
def __init__(self, name):
self.name = name
def set_name(self, new_name):
return ' '.join(s[0].upper() + s[1:] for s in new_name.split(' '))
def __main__():
me = Person('Foo')
me.name = me.set_name('foo bar')
比較的単純な例では、どちらの方法でも同じ結果が得られるはずです。私の質問は、どの方法がより良い実践と考えられるでしょうか?読みやすさやパフォーマンスの理由はありますか、それとも単に個人的な好みですか?
私が最初に学んだ言語の1つはパスカルでした。これには重要な違いがありました。 procedures があり、戻り値がありませんでした functions が always に戻り値のタイプがありました。
これらは、別々のことを行う2つの別々の構成要素です。
関数は値を返します。常に。
それが邪魔にならないようになりましたが、メソッド(それがfunction-esqueでもprocedure-esqueでも)は、何かを返す場合に意味のあるものを常に返す必要があります。時間の99%または100%で破棄される値を返すことは役に立ちません-それは無駄です。優れたコンパイラはこれを認識し、リターンが何もない場合でも最小限にすることができますが、コードを読む人にとっては human 時間の無駄です。それは何を返しますか?なぜ戻ってくるのですか?
多くの静的分析ツールは、(適切に)関数呼び出しの戻り値を無視することについてプログラマーに警告します。例 FindBugs
このメソッドは、複数のバイトを返すことができる
Java.io.InputStream.read()
のバリアントの1つの戻り値を無視します。戻り値がチェックされていない場合、呼び出し側は、呼び出し側が要求したよりも少ないバイト数が読み取られた場合を正しく処理できません。多くのプログラムでは、入力ストリームからの読み取りは通常、要求されたデータの全量を読み取るため、これは特に油断のならない種類のバグであり、プログラムが散発的にのみ失敗する原因になります。
または g ++の警告 (#534)
関数 'Symbol'の戻り値を無視(場所と比較)
値を返す関数は、たとえばステートメント自体やコンマ演算子の左側などの副作用のためにのみ呼び出されます。試してみてください:(void)function();関数を呼び出し、その戻り値を無視します。 fvr、fvo、およびfdrフラグも参照してください。
したがって、返される戻り値をする必要がありますであることが期待されます。意味がなければ、そもそもそうであってはなりません。それが例外的な失敗だとしたら、それが例外です(多くの言語で)。
さて、このコードについて何か見てみましょう:
_class Person:
def __init__(self, name):
self.name = name
def set_name(self, new_name):return ' '.join(s[0].upper() + s[1:] for s in new_name.split(' '))
def __main__():
me = Person('Foo')
me.name = me.set_name('foo bar')
_
私が呼び出したそれらの行を参照してください?彼らは彼らがしていると言うことをしていませんまったく。
Setという名前の関数は設定されていません。そのフォーマットは、設定されていません。
もちろん、_format_name
_を書くのは悪い方法ではありません。そしてそれは関数です-それは何かを返すからです。そして、(私のJavaを許して)public void setName(String name)
はvoid
戻り値型を持っています-誰にとっても役に立たないためです。それはその意図を明確に伝えています-「私はこの値を設定し、それを行います。心配する必要はありません。」
メソッドは有用なの戻り値のみを持つ必要があります。
me.name = me.set_name( 'foo bar')
これを行わないでください。
何がより良い実践か知りたい場合は、例を読んでください。最初:
今度は2つ目:
これらすべての要点は、ここでの問題は「メソッドが戻るのか」ではないということです。質問ではなく、それは「あなたがしていることをしますまったく意味がある」についてです。関数が何も返さない場合、void/no return関数は完全に意味があります。
そのため、set_name
メソッドに別の名前を付ける必要があるため、意味があるか、それを取り除く必要があります。現時点で問題があるのは、set_name
が非常に貧弱な関数であるためです。
あなたの例は次のいずれかである場合より良いでしょう:
__init__
は設定時に名前をフォーマットしましたset_name
が実際にフォーマットされた名前を設定した次のような例:
class Person:
def __init__(self, name):
self._name = self._format_name(name)
def _format_name(self, n):
return ' '.join(s[0].upper() + s[1:] for s in n.split(' '))
def __main__():
me = Person('Foo')
理にかなっています。名前の設定を許可してから、誰かに具体的に名前をフォーマットするように要求する理由を質問します。あなたも行うかもしれません:
class Person:
def __init__(self):
pass
def set_name(self, n):
self._name = ' '.join(s[0].upper() + s[1:] for s in n.split(' '))
def __main__():
me = Person()
me.set_name('foo')
方法2は忌まわしいです。ユーザーに実際の目的ではない追加の作業を行わせています。他の人が指摘したように、あなたは名前を設定していません。あなたはそれをフォーマットしています。少なくとも、クラスメソッドである必要があります。
class Person:
def __init__(self, name):
self.name = name
@classmethod
def format_name(new_name):
return ' '.join(s[0].upper() + s[1:] for s in new_name.split(' '))
def __main__():
me = Person('Foo')
me.name = me.format_name('foo bar')
しかし、それでも、ここでの問題は.name
は完全にパブリックプロパティです。ユーザーに名前のフォーマットについて知ってもらう必要があるのはなぜですか。
ゲッター/セッターの場合、値を返さないことが醜いという問題がある場合は、self
を返すことを検討してください。これにより、連鎖が可能になります。
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def set_name(self, new_name):
self.name = ' '.join(s[0].upper() + s[1:] for s in new_name.split(' '))
return self
def set_age(self, age):
self.age = age
return self
def __main__():
me = Person('Foo', 50)
me.set_name('foo bar').set_age(42)