惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

The Last Watchdog
The Last Watchdog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
S
Secure Thoughts
MongoDB | Blog
MongoDB | Blog
博客园 - Franky
T
Tor Project blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Google DeepMind News
Google DeepMind News
L
LINUX DO - 最新话题
博客园_首页
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Vercel News
Vercel News
Last Week in AI
Last Week in AI
月光博客
月光博客
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
P
Proofpoint News Feed
博客园 - 叶小钗
NISL@THU
NISL@THU
C
Check Point Blog
K
Kaspersky official blog
N
News and Events Feed by Topic
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
A
Arctic Wolf
T
Threatpost
GbyAI
GbyAI
L
LINUX DO - 热门话题
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
P
Privacy & Cybersecurity Law Blog
N
News and Events Feed by Topic
Scott Helme
Scott Helme
P
Privacy International News Feed
The Register - Security
The Register - Security
G
GRAHAM CLULEY
Recorded Future
Recorded Future
Apple Machine Learning Research
Apple Machine Learning Research
C
Cybersecurity and Infrastructure Security Agency CISA
B
Blog
Project Zero
Project Zero
Cyberwarzone
Cyberwarzone
Webroot Blog
Webroot Blog
Microsoft Security Blog
Microsoft Security Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
D
DataBreaches.Net
J
Java Code Geeks
AWS News Blog
AWS News Blog
Help Net Security
Help Net Security
Engineering at Meta
Engineering at Meta
M
MIT News - Artificial intelligence
T
Threat Research - Cisco Blogs
Google DeepMind News
Google DeepMind News

Secret Weblog

Becoming More Xee: A Modern XPath and XSLT Engine in Rust Looking for new challenges! Repeat Yourself, A Bit The Curious Case of Quentell The Humble For Loop in Rust The Humble For Loop in JavaScript Don Question Best Practices I Was a 1980s Teenage Programmer Part 5: Achieving Assembly I Was a 1980s Teenage Programmer Part 4: The Call of Assembly The Tooling Shift I Was a 1980s Teenage Programmer Part 3: MSX-2 JavaScript: when you need two ways to do it! Empowering Programming Languages Bloat and Retrofuturism Refreshing my Blog Again Random Rust Impressions Apilar: An Alife System I Was a 1980s Teenage Programmer Part 2: Olivetti M24 I Was a 1980s Teenage Programmer: the Alphatronic SolidJS fits my brain Is premature optimization the root of all evil? Framework Patterns: JavaScript edition Roll Your Own Frameworks Framework Patterns Secret Weblog Highlights Refactoring to Multiple Exit Points mstform: a form library for mobx-state-tree Seven Years: A Very Personal History of the Web Morepath 0.16 released! Is Morepath Fast Yet? Introducing Bob Strongpinion Punctuated Equilibrium in Software Morepath 0.15 released! Impressions of React Europe 2016 Morepath 0.14 released! Morepath 0.13 now with Dectate Dectate: advanced configuration for Python code JavaScript Dependencies Revisited: An Example Project The Incredible Drifting Cyber A Brief History of Reselect The Emerging GraphQL Python stack Thoughts about React Europe Build a better batching UI with Morepath and Jinja2 GraphQL and REST Server Templating in Morepath 0.10 10 reasons to check out the Morepath web framework in 2015 A Review of the Web and how Morepath fits in Morepath 0.9 released! Better REST with Morepath 0.8 Morepath 0.7: new inter-app linking They say something I don Life at the Boundaries: Conversion and Validation BowerStatic 0.4 released! Morepath 0.6 released! Morepath 0.5(.1) and friends released! New HTTP 1.1 RFCs versus WSGI Against On Naming In Open Source My visit to EuroPython 2014 Morepath 0.4.1 released (with Python 3 fixes) Morepath 0.4 and breaking changes Announcing BowerStatic Morepath 0.3 released! Morepath 0.2 Morepath Python 3 support The Call of Python 2.8 Morepath 0.1 released! WebOb and Werkzeug compared Morepath: from Werkzeug to WebOb Racing the Morepath: SQLAlchemy Integration The Centre Cannot Hold Breaking Morepath Changes Morepath Update How to do REST with Morepath Morepath Security the Gravity of Python 2 #python2.8 discussion channel on freenode Alex Gaynor on Python 3 Morepath Documentation Starting to Take Shape Back to the Center Morepath App Reuse Implementing Grok Grok: the Idea Why Linux Works for Me On the Morepath Reg, Now With More Generic! The New Zope as a Web Framework Jim Fulton, Zope Architect Renewing Zope Object Publishing The Weirdness of Zope The Rise of Zope My Exit from Zope Reg: Component Architecture Reimagined JSConf EU 2013 impressions Obviel 1.0! JS Dependency Tools Redux Succinct data structures
The Story of None: Part 4 - Guard Clauses
Martijn Faassen · 2013-02-01 · via Secret Weblog

part 1 part 2 part 3 part 4 part 5 part 6

Last time...

Last time in the Story of None we ended up with this validation function:

def validate_end_date_later_than_start(start_date, end_date):
    if start_date is None and end_date is None:
        return
    if start_date is None:
        return
    if end_date is None:
        return
    if end_date <= start_date:
        raise ValidationError(
            "The end date should be later than the start date.")

So let's apply some boolean logic and rewrite all this to be slightly shorter (but still readable):

def validate_end_date_later_than_start(start_date, end_date):
    if start_date is None or end_date is None:
        return
    if end_date <= start_date:
        raise ValidationError(
            "The end date should be later than the start date.")

Guard Clauses

What we've written above with the if ... return statement is a guard clause: a check at the beginning of a function that returns early from the function if the condition is true.

Instead we could have written the function without guard clauses, like this:

def validate_end_date_later_than_start(start_date, end_date):
    if start_date is not None and end_date is not None:
        if end_date <= start_date:
            raise ValidationError(
                "The end date should be later than the start date.")

or like this:

def validate_end_date_later_than_start(start_date, end_date):
    if (start_date is not None and
        end_date is not None and
        end_date <= start_date):
            raise ValidationError(
                "The end date should be later than the start date.")

I think both alternatives are a lot less clear than the one with the guard clause in it.

With a function it's better if the main thing that it is doing, in this case comparing end_date with start_date, is also the main unindented block. This makes the function easier to read. If you make the main behavior of the function be inside some other if clause, it becomes slightly harder to read.

The second alternative doesn't create this second level of indentation, but still makes the comparison between start and end date rather hidden among the is not None clauses. This makes the expression a bit harder to think about compared to the version of the function that uses the guard clause.

Guard and Stop Worrying

The great advantage of a guard clause is that you can forget about the stuff you're guarding against in the code you write after the guard clause. So once we have passed the guards, we can entirely stop worrying about start_date or end_date being None; we've already guarded against those cases and handled them.

This is great! We can compare start_date and end_date freely. We can call methods on start_date and end_date safely. We can pass start_date and end_date to other functions and they don't need to worry about them being None either, unless of course such functions can be called from somewhere else where it could be None.

We worry about None so we can stop worrying about None when it matters. Ah, such a relief! Thank you guard clauses!

We'll talk a bit more about guard clauses next.

part 1 part 2 part 3 part 4 part 5 part 6

Brutus_dmc

What about try/except? Something like this:

def validate_end_date_later_than_start(start_date, end_date):
  try:
    if end_date <= start_date:
      raise ValidationError(
        "The end date should be later than the start date."
      )
  except TypeError:
    if start_date is not None and end_date is not None:
      raise

Martijn Faassen

I wouldn't do it this way either. You have to go through extra work in the catch handler to make sure it's not some other TypeError you are dealing with (of course with this particular logic that's less likely, but a real problem in other cases). I find the logic much harder to understand too. Compare a date, if there's a TypeError, then check whether the start_date and the end_date are not-None, then raise the error again?

Plus this only works for guard clauses that raise exceptions. What do you do when a return value is in play?