日付が有効かどうかを確認するプログラム

私は初心者プログラマで、現在は本を通して自分でPythonプログラミングを学んでいます。私は章の中で質問に出くわしました、そしてそれに対する解決策を見つけるのに2日かかりました。それは問題のための効率的なコードではないかもしれませんが、みんながそれをレビューすることができれば私は感謝するでしょう。プログラミングに関する知識のレベルを皆さんに知らせるために、関数、シーケンス、および意思決定構造に関する基本的な知識があるだけです。

日付が有効かどうかを確認するプログラムを作成しますか。日付は   mm/dd/yyyy形式です。

     

擬似コード:

     
      
  • 月、日、年の有効性をチェックする3つの関数を定義します。有効な場合は各関数に対してTrueを返します。
  •   
  • 「/」で区切られた文字列dateを入力し、それを日付変数に割り当てます。
  •   
  • 日付変数を3つの変数に分割します。
  •   
  • 3つの変数がTrueかどうかを確認します。
  •   
  • 日付が有効かどうかを印刷します。
  •   

私は2月が29日であるうるう年を考慮に入れておらず、また西暦1年から現在の年までの年を想定していました。

def monthcheck(month):
        if month > 0 and month <= 12: ## If month is between 1 and 12, return True.
            return True
        else:
            return False



def daycheck(month,day):
    monthlist1 = [1,3,5,7,8,10,12] ## monthlist for months with 31 days.
    monthlist2 = [4,6,9,11] ## monthlist for months with 30 days.
    monthlist3 = 2 ## month with month with 28 days.

    for mon in monthlist1: ## iterate through monthlist1.
        if month == mon: ## Check if the parameter month equals to any month with 31 days.
            if day >=1 and day <= 31: ## If the parameter day is between 1 and 31, return True.
                return True
            else:
                return False

    for mon in monthlist2: ## iterate through the monthlist with 30 days.
        if month == mon: ## check if the parameter month equals to any month with 30 days.
            if day >= 1 and day <= 30: ## if the parameter day is between 1 and 30,return True.
                return True
            else:
                return False

    if month == monthlist3: ## check if parameter month equals month with 28 days.
        if day >=1 and day <= 28: ## if the parameter day is between 1 and 28,return True.
            return True
        else:
            return False



def yearcheck(year):
    if len(year) >= 1 and len(year) <= 4: ## Check if year has between 1 to 4 numbers and return True.
        return True
    else:
        return False


def main():
    date = str(input("Enter the date in mm/dd/yyyy format: ")) ## Input date in the given format.

    month,day,year = date.split("/") ## split the date into 3 separate variables.

    monthvalidity = monthcheck(int(month)) 

    dayvalidity = daycheck(int(month),int(day)) 

    yearvalidity = yearcheck(year) 

    if monthvalidity == True and dayvalidity == True and yearvalidity == True: ## check if all 3 variables are valid or True
        print("The date {0} is valid.".format(date))

    else:
        print("The date {0} is invalid.".format(date))

main()
18
@ダニエル誰かがこれを指摘すると確信していましたが、(1)これは当てはまりません、(2)あなたのコメントはこれらのケースに対してより良い解決策を提示します。私はあなたが を書くべきであることを再確認します。真偽値リテラル
追加された 著者 Brad Tutterow,
@アロ:いいえ。あなたが物事をもっと複雑にしたいのであれば、その時だけ必要です。 OPはうるう年の計算を無視しますが、それほど難しいことではなく、うるう年の規則をコメントで記述しても問題ありません。では、どの「偽りの問題」が本当に問題なのでしょうか。
追加された 著者 user12462,
問題の説明の擬似コード部分は何ですか。
追加された 著者 user12462,
ところで: ISO 8601 は現在30歳です。
追加された 著者 Denis,
@ alloのコメントに投票できないし、十分に高いリンク。日時は狂気です。私はこれが好きです:2011-12-30はサモアの無効な日付です。 :-)
追加された 著者 tony722,
@alloあなたは正しいですが、1.ある場合には、これらの のような単純なチェックで十分です(つまり、どのタイプの日付を入力として使用するかをある程度制御できる場合)、2.これこれは本からのエクササイズなので、これがPythonでよくなるためのプログラミング問題としてのみ意図されている場合、OPがあらゆる可能性のあるエッジケースを処理することを期待するのはかなり不公平です。
追加された 著者 Daniel,
@Konrad Rudolphは必ずしもそうではありません、 foobool でない場合、これは成り立ちません。もちろん、その場合は単に bool(foo)を返すべきですが、覚えておくべきことです。
追加された 著者 Daniel,
@ miracle173これは解決策の一部です。
追加された 著者 Michael Shi,
特にalloのコメントを支持するためにこのコミュニティに参加しました。奇妙なタイムゾーン、うるう秒、カレンダーの変更については、こちらの動画もご覧ください。理論的な物理学的な意味では「難しい」という意味で、日付を適切に検証することは難しい問題です。
追加された 著者 Rusty Shackleford,
@ダニエル:私はあなたがあなたのソフトウェアのすべてのエッジケースを扱うべきであるか、または扱うことができるとは思わない。これは…地獄でしょう。しかし、少なくともうるう年の処理は持っているのがいいでしょうし、4/100/400のルールでは実生活のための素晴らしいエクササイズです;-)。この記事の残りの部分では、次に日付関連の何かを実装するときにこれらの問題について聞いたことがあり、それらを考慮する必要があるかどうかを検討します。
追加された 著者 Fabian Tamp,
リストの簡単な例としては、うるう年をまったく扱っていないので、有効な日付を拒否します。始めると、4で割り切れる年にうるう年ではない、すなわち100で割り切れるという特別な規則があることに注意してください。コメントに収まらないもっと多くの例外があります。日付計算はとても難しいです。
追加された 著者 Fabian Tamp,
地獄へようこそ。日付を検証することは、これまでで最も難しい(プログラミング)問題の1つです。たとえば、年がグレゴリオ暦が有効な日付の範囲内であるかどうかをチェックしているのではありません。したがって、存在しない日付に対してはtrueを返すことができます。プログラマーが時間について信じている虚偽の記述について読んでくださいこちらここ
追加された 著者 Fabian Tamp,

8 答え

よくできました!確かに改善の余地はありますが、それを突き出して自分で解決策を見つけたのは良いことです。

  1. def monthcheck(month):
        if month > 0 and month <= 12: ## If month is between 1 and 12, return True.
            return True
        else:
            return False
    

    Can be more succinctly written as

    def monthcheck(month):
        return 0 < month <= 12
    
  2. While writing comments for practically every line of code can help you learn, and is a good way to keep track of where you are when working through pseudocode, don't just write what the code already told you.

  3. PEP 8 - the style guide for Python - recommends using snake_case for naming functions, so monthcheck would become month_check.

  4. What happens if I input "test" when asked for a date? The program crashes. It's a good idea to validate that any input from the user is in the format you expect.

  5. The program asks for input in the format mm/dd/yyyy but will accept m/d/y, which probably isn't desired.

  6. Don't compare explicitly to True/False

    if monthvalidity == True and dayvalidity == True and yearvalidity == True:
    

    Can be more easily read if it is written as

    if monthvalidity and dayvalidity and yearvalidity
    
  7. daycheck has several locations in which it could be improved. You could use a dictionary (as heather does), but if you haven't learned about those yet, you can still improve the algorithm.

    • Python's in operator can also check if a value is in a list, so instead of looping over monthlist1 and monthlist2, you can just check if month in monthlist1.
    • Since monthlist1 doesn't really describe what is in the variable, and I am having difficulty coming up with a better name, I would just inline the list with if month in [1, 3, 5, 7, 8, 10, 12] and add a clarifying comment.
  8. Don't be afraid to use natural sounding variable names. I believe monthcheck is better named is_month_valid.

  9. What about negative numbers? 1/1/-111 is apparently a valid date. This might be desired, but probably deserves a comment.

  10. Converting numbers to strings to determine the size is generally not a good idea, and can get you into trouble in more complex cases with floats/doubles. I would prefer the check 1 <= year < 10000.

  11. There is no need to have str() around an input function, input returns a string already.


Pythonには、「電池内蔵」言語として、これを可能にする機能があります。プロダクションコードでは、これがあなたが使うべきものです。

from datetime import datetime

def main():
    user_input = input('Enter the date in mm/dd/yyyy format: ')

    try:
        datetime.strptime(user_input, '%m/%d/%Y')
        print('The date {} is valid.'.format(user_input))
    except ValueError:
        print('The date {} is invalid'.format(user_input))


if __name__ == '__main__':
    main()

これはかなり長かったです、しかし心配しないでください!あなたが始めているときにたくさんのフィードバックを得るのは完全に正常です。コーディングするほど、自然になります。

23
追加された
@CJDennis私もPythonプログラマーではありません。しかし、 verb_name という慣用句が一般的ですが、読みやすくなるように改善することができると思います(これが私のポイントでした)。 OTOH、そのようなすべてのフラグの前に似たような言葉遣いを付けると、その目的を簡単に識別できるようになります。もちろん、それはハンガリー記法に関する議論でよく取り上げられています。
追加された 著者 James,
マイナーニット。 is_month_valid の代わりに、読みやすいので month_is_valid を使用すると思います。 if month_is_validおよび...
追加された 著者 James,
いくつかの提案:datetime import datetimeからの を使うことであなたのコードをもう少し冗長にすることができます、そしてあなたがPython 3.6+を持っているなら、 print(f "date {date:%m /%d /%Y}は有効です。 ")。最後に、ちょっとしたメモ - date はdatetimeの予約語です、私はしばしば 'date'の代わりに 'today'または 'date_val'を使います。もちろん、datetime import datetime、date からを実行しないのであれば問題ありませんが、名前の衝突を避けるのに役立ちますので、ライブラリではなく期待される変数に取り組んでいます。誤って機能します。
追加された 著者 thomas nn,
@ rcollyer Codeは英語ではありませんが、わかりやすく、特定の規則に従う必要があります。より柔軟なルールは混乱を招きます。 year_is_leap にする必要がありますか?それは month_is_valid のパターンに従いますが、それがなぜ英語の文法を深く理解しなければうまくいかないのかを言うのは難しいと思います。別の例として、 should_send_emailishas は意味がありません)では問題ありませんが、 email_should_be_sent あまりにも面倒です。これは、変数名に名詞を使用するのと同じハンガリーの表記法です。
追加された 著者 davitenio,
@ rcollyer私はPythonプログラマーではありませんが、はい/いいえの動詞を前に付けてブール変数に名前を付けるのはごく普通のことです。 has_time などの場合は、「有効な月であるかどうか」と頭の中で読みます。
追加された 著者 davitenio,
ありがとうございます。私はPythonをあまり使っていないので、学ぶことがたくさんあるようです。
追加された 著者 user142650,
貴重なご意見ありがとうございました@ Gerrit0。それは私がもっと学び続け続けることに本当に動機を与えました。私は実際には辞書について学んでいませんし、私は実際に例外処理を吸収していません。次回コードを投稿するときは、それを改善するように努めます。私のコードをレビューする貴重な時間を取っておいていただきありがとうございます。
追加された 著者 Michael Shi,

ほとんどの場合、私にはかなりよさそうです!

フォーマット

あなたはたくさんの変数名で単語を区切るためにアンダースコアを使わなかった、それはちょっと私を悩ませました。 Snake_caseはPEP8で推奨されているので、それを変更しました。

あなたはまたあなたの最初の関数に余分なインデントを追加しました(それがあなたのコードを問題の中にコピー&ペーストしたものかどうかにかかわらず)、私はそれを削除しました。

アルゴリズム

あなたの day_check が私が最も変更したものでした。私は基本的に月の数字と日の数字を一致させる辞書を作成し、関数に入力された日が0と辞書で定義された最大値の間にあるかどうかをチェックしました。機能が大幅に短縮され、明確化されたと感じました。

私が行ったもう1つの変更は、mainの if ステートメントを if month_validityとday_validityおよびyear_validity:に変更したことです。

main()を使用している場合

main()を呼び出すときに、正しい位置にいることを確認してください。最後の if ステートメントを追加しました。

貧しい2月

うるう年は、チェックするのに悪くありません。単純に month_check にパラメータとして年を追加してください。

if int(year) % 4 == 0:
    if int(year) % 100 == 0:
        days_in_month[2] = 28
    else:
        days_in_month[2] = 29
else:
    days_in_month[2] = 28

ユーザー入力チェック

Gerrit0が指摘したように

What happens if I input "test" when asked for a date? The program crashes. It's a good idea to validate that any input from the user is in the フォーマット you expect.

This can be fixed with a simple try/except - try all the code in the main() function, except ValueError and print a reminder to the user of the proper フォーマット.

負の日付

Gerrit0はまた、この関数は年内に負の数を許可する(このXKCDを思い出させる)ことを指摘しています。これをコードに追加しませんでしたが、年関数を変更して実際の数値が正しい範囲内にあるようにします。これにより、すべての型変換を直接入力ではなく入力で行うことができます。各機能入力の前に。日付がグレゴリオ暦であることを確認するための優れた方法もあります。

最終コード

def month_check(month):
    if month > 0 and month <= 12: ## If month is between 1 and 12, return True.
        return True
    else:
        return False



def day_check(month, day):
    days_in_month = {1:31, 2:28, 3:31, 4:30, 5:31, 6:30, 7:31, 8:31, 9:30, 10:31, 11:30, 12:31}
    if 0 < day <= days_in_month[month]:
        return True
    else:
        return False



def year_check(year):
    if len(year) >= 1 and len(year) <= 4: ## Check if year has between 1 to 4 numbers and return True.
        return True
    else:
        return False


def main():
    date = str(input("Enter the date in mm/dd/yyyy フォーマット: ")) ## Input date in the given フォーマット.

    try:
        month,day,year = date.split("/") ## split the date into 3 separate variables.

        month_validity = month_check(int(month)) 

        day_validity = day_check(int(month),int(day)) 

        year_validity = year_check(year)

        if month_validity and day_validity and year_validity: ## check if all 3 variables are valid or True
            print("The date {0} is valid.".フォーマット(date))

        else:
            print("The date {0} is invalid.".フォーマット(date))
    except ValueError:
        print('Your input was not valid. Please enter the date in a mm/dd/yyyy フォーマット.')

if __name__ == "__main__":
    main()
8
追加された
@ mathlover92もっと基本的なものが欲しいならcodecodemyのpythonコースを、単純な問題がしたいのならEuler Problemsを試してください。
追加された 著者 projectshave,
@ mathlover92 [...]それらが正しいものであるかどうかに関わらず、あなたのプログラムが期待通りに振る舞わないなら、他のエラー(または私達の場合はユーザー入力とは無関係のValueErrors)を見ることはできませんポップアップします。それはそれらを使用することについて注意することの一つです。 Tl; dr、例外を検索=)
追加された 著者 projectshave,
@ mathlover92は、例外処理を使用するときに、例外をキャッチしない限り、非常に計算コストが高いわけではないことに注意してください。 そのため、ここでのように、ユーザーエラーが発生したときに例外を検出するだけの場合は、それを使用するのが良いでしょう。基本的にはコードを実行するだけで、エラーが発生した場合は ValueError エラーが発生した場合の対処方法を説明するコードの他の部分に入ります。 ValueError以外のエラー other が発生した場合は、それが表示されます。これが、一般に try:... except:... を実行したくない理由です。それはそれがすべてのエラーをキャッチすることを意味します、[...]
追加された 著者 projectshave,
@ mathlover92したがって、 if __name__ == "__main__":が実行するのは、そのファイルの場所からそのファイルを実行しているかどうか、つまりインポートしているかどうかを確認することです。あなたがファイルをインポートしているなら、それがどんな「ゆるい」コードでも走らせるので、これは役に立ちます。したがって、 main()関数をfilename import main からを実行して別のファイルにインポートしたい場合は、自動的に main が実行されます。それが欲しくないです。 if ステートメントはそれを防ぐためのものです。そして、ええ、入力チェックは例外処理であり、それはプログラムの流れを制御するために非常に便利です[...]
追加された 著者 projectshave,
「うるう年をチェックするのはそれほど悪くありません」、それでも2000年はうるう年でした。
追加された 著者 Pere,
私のコードをレビューしていただきありがとうございます。関数と変数に名前をつけるための正しい方法を実装し始めました。 main()を使用する部分がわかりませんでした。入力チェックは、例外処理に該当しますか。その部分についてもっと読みます。
追加された 著者 Michael Shi,
ありがとうございます@私は例外処理を実践するためのいくつかの問題を見つけることができますので、私はそれをしっかり把握することができます..そして例外処理はほとんどすべてのプログラムで使用されていますか?初心者にpythonプログラミングの問題を練習するための良いサイトを勧めますか?
追加された 著者 Michael Shi,

単純化された関数で heather's からの回答を続けてください。

簡易月チェックの返品:

def month_check(month):
    return 0 < month <= 12

これはday_checkの異なる実装で、辞書ではなく普通のリストを使っています。

def day_check(month, day):
    days_in_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
    return 0 < day <= days_in_month[month-1]

簡略化されたyear_checkリターン

def year_check(year):
    return 1 <= len(year) <= 4
4
追加された

これはOPの質問には答えませんが、 dateutil 多くの異なる日付文字列スタイルを日付(または日時)オブジェクトに解析するためのショートカットとして。

from dateutil.parser import parse

def valid_date(date_value):
    try:
        return parse(date_value).date()
    except ValueError as e:
        logger.exception(f"Not a valid date: '{date}' - reason: {e}.")
3
追加された

ここには本当に素晴らしい答えがありますが、私はコメントを見たときに(そして私が1つ追加したいと思うときに)どう思うかをお見せしたいのです。

コメントは、コードを明確にしたいときに通常使用されます。しかし、   あなたのコードは十分に明確です、コメントは冗長になります。そして   冗長なものを読むことは、私たちが無駄になっているような気分にさせる   時間があるので、当然のことながらコメントは不要です。削除可能です。

私はあなたがここで他の人によって与えられた答えを理解したと思うでしょう、それで私はコメントに集中します:

def is_month_valid(month):
    return 0 < month <= 12 ## If month is between 1 and 12, return 

コードよりも明確であるためにコメントを残しておく必要がある状況がある場合:コードをそのコメントのように見せる

例えば、あなたは考えてみることができます:

return 0 < month <= 12    ## If month is between 1 and 12, return
return 1 <= month <= 12 
return is_between(month, 1, 12)
return is_the(month).between(1, 12)
return comparable(month).is_between(1, 12)  ## Or you could call is_month_valid(comparable(month)) instead and this gives you...
return month.is_between(1, 12) ## If month is between 1 and 12, return
## Also, if month.is_between(1, 12): return... I think the other answers actually answers why this should be discarded. If not, keep reading and think why.

それらはすべて有効な解決策です。そして、あなたが見るならば、最後のものはコメントを本当に冗長にするでしょう:

def is_month_valid(month):
    return month.is_between(1, 12) ## If month is between 1 and 12, return 

それでは、なぜあなたはこのコメントを続けますか?コードは明確です、そして実際に、そのコメントはあなたのコードを汚く見えるようにしているので、あなたはそれを削除する以外に別の選択肢はありません。

そうだよ!! ...修正? ...これはいいですか?読みやすさだけに焦点を当てています。ではない正確に。 各ソリューションの複雑さを覚えておく必要があります。そう:

return 0 < month <= 12    ## If month is between 1 and 12, return

これはかなり良い解決策です。どうして?コメントを冗長にするという点では読みやすいです。そして、あなたはそれを機能させるために他に何も必要としません。しかし

return 1 <= month <= 12    ## If month is between 1 and 12, return

これはいいですか?コメントは今より冗長です。そして、あなたは複雑さを増しませんでした。これは無料の変更のようなものです。また、これは有効な月として(0、1)の間の数を許可しません。だから、はい、これは実際に良いです。

return is_between(month, 1, 12) ## If month is between 1 and 12, return

まあ、少なくとも私にとっては、これは以前のものより読みにくくなっています。そしてそれを機能させるためには別の関数を作成する必要があります。この機能は十分価値がありますか?さて、あなたがそれを作成すれば、 is_between(year、1、10000)のように、その関数をさまざまな場所で再利用できると言う人もいるでしょう。そしてそれはあなたがここで整数の検証を集中化することを可能にします... しかし、あなたが決める必要があります。それは実際にはプログラミングです:決断を下し、それらを正当化します。物事に名前を付けるのと同じように...

return is_the(month).between(1, 12) ## If month is between 1 and 12, return

これは実際にはもっと冗長で読みやすくなっています。しかし今、あなたは議論しやすい名前で class (別の関数)を作成しました、そして( method のように)関連する別の関数も作成しました。しかし、これは最後の解決策からの長所を維持します。現時点で、これは不必要な過剰設計されたソリューションのように感じ始めています、しかし私達はちょうど私達の考えを説明しています。

return comparable(month).is_between(1, 12) ## If month is between 1 and 12, return

なぜ is_the は間違いなく名前なのでしょうか。しばらくして、 is_the という関数が表示されると思います。私の主な反応は「一体何なのか」です。何も言わない。内部のコードを読まなければ、その目的を知ることはできません。そのため、この解決策では、おそらく間違いなく名前が付けられています。 month は1と12に匹敵しています。しかし、これはまだ少し利点があるだけです( dry に役立ちます)。

return month.is_between(1, 12) ## If month is between 1 and 12, return

今、これは良いです。そのコードは、以前のソリューションよりも冗長で読みやすいです。しかし、その価格はいくらですか?現在、 is_month_valid を呼び出すたびに、まず同等のオブジェクトを送信する必要があります。 is_month_valid(同等(月))。そして、これを機能させるためにも similaris_between を作成する必要があることを忘れないでください。しかし、これはオーバーエンジニアリングソリューションが必要ですか?これはあなたのプログラムでこれがどれほど必要か、つまりこれが dry にどれだけ役立つか、そしてこれからどう変わるかにかかっています。安定していない場合は、修正よりも多くの問題が発生する可能性があります。

Of course, for every solution exists a better one at a particular time. To get them you just have to continue over engineering it and balance their pros and cons. I will not continue doing that because i think is clear now. And looking at the size of your program, i'm sure the last solution is too much, while the second one (1 <= month <= 12) is simple and clear.


そして、あなたが持っているすべてのコメントに対して同じことを続けてください。

monthlist1 = [1,3,5,7,8,10,12] ## monthlist for months with 31 days.
monthlist2 = [4,6,9,11] ## monthlist for months with 30 days.
monthlist3 = 2 ## month with month with 28 days.

それらの行はこれに置き換えられないでしょうか。

months_with_31_days = [1,3,5,7,8,10,12] ## monthlist for months with 31 days.
months_with_30_days = [4,6,9,11] ## monthlist for months with 30 days.
month_with_28_days = 2 ## month with month with 28 days.

今、冗長なコメント、彼らはあなたのコードをいじっています。

months_with_31_days = [1,3,5,7,8,10,12]
months_with_30_days = [4,6,9,11]
february = 2 ## And month_with_28_days? Isn't that just february?.

また、 @heather が言ったように、私はこれの代わりに days_in_month 関数を作ることを好むでしょう。しかし、ここでの私の焦点は、コメントに直面したときに私がどう思うかを示すことです。


for month in monthlist: ## iterate through monthlist.

これは消えていくだろうが、そのコメントは実際に必要なのだろうか? monthlistの(各)月のmonthlistを反復処理するよりも明確ではありませんか。たいていのプログラマーは、この行を読むだけで、この行が何をしているのかを認識するでしょう。この場合、コメントは必要ないと思います。


if month == mon: ## Check if the parameter month equals to any month with 31 days.

このコードを理解するには何らかの文脈が必要なので、これは興味深いコメントです。あなたはそれを読むことができず、何が起こっているのか知ることができません。あなたはこの写真が必要です:

def is_day_valid(month, day):
    ...
    for mon in months_with_31_days:
        if month == mon: ## Check if the parameter month equals to any month with 31 days.

これで、 month の代わりに mon を使用する理由、および month 変数が追加された理由を理解できます。 この状況。行を理解するために何らかの文脈が必要なとき。望ましくない。幸いなことに、理解のために必要な文脈でコメントを入れましたが、必要ですか。これは @ Gerrit0 によって解決されました。

if month in months_with_31_days: ## Check if the parameter month equals to any month with 31 days.

そのコメントはノイズを追加します!それを削除してその完璧な。 for が必要な場合はどうすればよいですか。

for month_with_31_days in months_with_31_days:
    if month == month_with_31_days: ## Check if the parameter month equals to any month with 31 days.

必要なら if 行を読みながら、その for 行を忘れることが可能です。どの 31日の月でも構いませんが、 if ブロック内に入力したかどうかは month に31日あるのでわかります。 。そして、その単一行の読みやすさを向上させるために以前に行ったようなソリューションをオーバーエンジニアすることができます...例えば if days_in(month)== 31:の場合、前のを移動します。 is_day_valid行。 機能を短く読みやすくする。各関数のコード行数が少ないほど、より多くのコンテキストが必要になる可能性が低くなります。


これはどうですか?

date = input("Enter the date in mm/dd/yyyy format: ") ## Input date in the given format.

その行を読むと、ユーザーに入力を求め、それを date という変数に保存していることがわかります。あなたは実際にそのコメントでより少なく言っています。しかし、冗長で不要です。そして、そのユーザーインタラクションを別の機能に分割することについてはどうでしょうか。例えば:

date = ask_user_for_input_a_date()

冗長すぎて、この場合は不要です。しかし、私はあなたが複数の入力を持って始めたとき、あなたはそれらの入力をどこに尋ねるべきか自分自身に尋ね始めるであろうことを指摘したい。出力でも同じことが起こります。初心者のための一つの良い習慣はあなたの main 関数を以下のように分割することです:

def main():
    inputs = take_inputs() ## You could validate inputs inside if you want
    solution = solve_problem(inputs) 
    show(solution)

問題を始める前に必要な情報をすべて集め(入力をグループ化)、問題を完全に解決し(出力をグループ化)、最後に解決策を示します。あなたが入力に関連した問題を抱えているなら、あなたはどこから探し始めるべきか知っています。同じことがあなたの問題のコアロジックと最終的なプレゼンテーションにも当てはまります。これは良い出発点であり、ユーザーによる操作が不要な問題には十分に適しています。


しかし、本気で始めましょう。私は私のプログラミングの人生を同じ問題で始めました。 日付の検証、そして私のコードはあなたのものよりずっと悪いものでした:)

3
追加された

あなたが上手にやったこと

コードを main 関数と3つのヘルパー関数にパッケージ化しました。それぞれに特定の目的があります。

スタイル

  • The most important function to define for this task, is_valid_mmddyyyy(date), isn't defined! That would make your work reusable.

  • The comments are excessive, and mainly serve to guide Python beginners. Instead of those comments, it would be better to document the purposes of the functions instead, as docstrings.

  • You should rarely need to write True or False explicitly in your code. The pattern

    if day >= 1 and day <= 31:
        return True
    else:
        return False
    

    … would be better written as return 1 <= day <= 31.

    Similarly,

    if monthvalidity == True and dayvalidity == True and yearvalidity == True:
    

    … would be better written as

    if monthvalidity and dayvalidity and yearvalidity:
    
  • The names monthcheck, daycheck, and yearcheck don't quite convey how they behave for valid or invalid inputs. Do they print error messages? Do they raise exceptions? It would be better to name them is_valid_month, is_valid_day, and is_valid_year to make it clear that they are predicates (functions that return either True or False with no side-effects).

  • It's annoying that monthcheck(month) and daycheck(month, day) accept integers as inputs, but yearcheck(year) accepts a string.

  • monthlist1 and its friends are not optimally named, thus necessitating comments like ## monthlist for months with 31 days. Also, monthlist3 is, deceptively, not a list. A slightly better idea:

    months_with_31_days = [1, 3, 5, 7, 8, 10, 12]
    months_with_30_days = [4, 6, 9, 11]
    months_with_28_days = [2]
    
    if month in months_with_31_days:
        return 1 <= day <= 31
    if month in months_with_30_days:
        return 1 <= day <= 30
    if month in months_with_28_days:
        return 1 <= day <= 28
    

検証品質

  • 入力には2つの/文字があると仮定します。より少ない部分またはより多くの部分がある場合、それは ValueError でクラッシュします。

  • 月または日が整数ではない場合、 Valueuerrrrでクラッシュします。

  • 年間の長さだけが気になるので、 1/1/zero1/1/3.14 はどちらも有効な日付と見なされます。

  • あなたが言うように、あなたはうるう年をサポートすることに悩まされていません。しかし、 daycheck 関数の設計により、うるう年のサポートを追加するためにコードを拡張することは困難です。

提案された解決策

主な課題は、月に何日あるかを判断することです。あなたの daycheck はそれをしますが、二つの点で不十分です。

  • が有効であると仮定します。 daycheck がその仕事をするためには、とにかくルックアップを実行する必要があるので、 monthcheck は実際には別の関数である必要はありません。月がルックアップテーブルにないことを示すだけです。

  • うるう年をサポートしたいのであれば、月だけでは不十分です。その年も知っておく必要があります。

それを念頭に置いて、私はこのようにプログラムを再設計するでしょう:

def days_in_month(year, month):
    """
    Given a year and month as integers, return the last day of that month
    (assuming the Gregorian calendar).  Raise ValueError if the month is
    invalid.
    """
    if month == 2:
        is_leap_yr = (year % 4 == 0) and (year % 100 != 0 or year % 400 == 0)
        return 29 if is_leap_yr else 28
    try:
        return {
            1: 31,        3: 31,  4: 30,  5: 31,  6: 30,
            7: 31, 8: 31, 9: 30, 10: 31, 11: 30, 12: 31
        }[month]
    except KeyError:
        raise ValueError('Invalid month')


def is_valid_mmddyyyy(date):
    """
    Test whether the date is a string in mm/dd/yyyy format representing a valid
    date (assuming the Gregorian calendar).
    """
    try:
        mm, dd, yyyy = [int(part) for part in date.split("/")]
        return 0 < yyyy <= 9999 and 0 < dd <= days_in_month(yyyy, mm)
    except ValueError:
        # Too few slashes, too many slashes, non-integer part, or invalid month
        return False


def main():
    date = input("Enter the date in mm/dd/yyyy format: ")
    valid = is_valid_mmddyyyy(date)
    print("The date {0} is {1}.".format(date, "valid" if valid else "invalid"))

if __name__ == '__main__':
    main()
2
追加された

ほぼすべてが前の投稿された答えによって既に言われています。しかし2つのメモ:

  1. if you really use names like monthlist1, monthlist2, monthlist3 then use them in a consistent way: if monthlist1 and monthlist2 are lists of numbers then monthlist3 should be a list of numbers, too。 Such a consistent naming makes it easier to read the code。 So

    monthlist3=[2]

  2. How to calculate the days of february correctly?

# calculate the days of february
if year%4==0:
    if year%100==0:
        if year%400==0:
            days = 29
        else:
            days = 28
    else:
        days = 29
else:
    days = 28
1
追加された

ここでのほとんどのことはすでにカバーされています。年と月のチェックはかなり簡単ですが、日のチェックは難しいです。

def daycheck(year, month, day):
    """
    Check if the day is valid.

    Parameters
    ----------
    year : int
    month : int
    day : int

    Returns
    -------
    is_valid : bool
    """
    assert  1 <= month <= 12
    max_days_per_month = {1: 31, 2: 30, 3:31,
                          4: 30, 5: 31, 6:30, 7: 31,
                          8: 31, 9: 30, 10: 31, 11: 30, 12: 31}
    if (day > max_days_per_month[day]) or (day <= 0):
        return False
    if month != 2:
        # Not quite true, e.g. September 1752 in the UK had less than 20 days.
        return True  
    dst_applies = (year % 4 == 0) and (year % 100 != 0 or year % 400 == 0)
    if not_dst_applies and day >= 29:
        return False
    return True

学ぶこと:

  1. Use dictionaries. They make things shorter/easier to understand
  2. Yes, February once had 30 days: https://en.wikipedia.org/wiki/February_30#Swedish_calendar
  3. Yes, if a local date is valid depends on the city/country, because of calendar system switches.
  4. It is capture error cases/easy cases in the beginning. The less deep your code is nested, the easier it is to read/check.
0
追加された