4151 lines
401 KiB
HTML
4151 lines
401 KiB
HTML
<!DOCTYPE html>
|
||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<link href="https://files.realpython.com" rel="preconnect" />
|
||
<title>
|
||
Python 3.10: Cool New Features for You to Try – Real Python
|
||
</title>
|
||
<meta name="author" content="Real Python" />
|
||
<meta name="description" content="In this tutorial, you'll explore some of the coolest and most useful features in Python 3.10. You'll appreciate more user-friendly error messages, learn about how you can handle complicated data structures with structural pattern matching, and explore new enhancements to Python's type system." />
|
||
<meta name="keywords" content="" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, viewport-fit=cover" />
|
||
<link rel="stylesheet" href="/static/realpython.min.4b158d0d7cd2.css" />
|
||
<link rel="stylesheet" href="/static/gfonts/font.5ac42994de49.css" />
|
||
<link rel="stylesheet" href="/static/glightbox.min.9b438b29cef1.css" as="style" onload="this.onload=null;this.rel='stylesheet'" /><noscript>
|
||
<link rel="stylesheet" href="/static/glightbox.min.9b438b29cef1.css" /></noscript>
|
||
<link rel="preload" as="image" href="https://files.realpython.com/media/Python-3.10-Cool-New-Features-for-You-to-Try_Watermarked.e2782d8a16dc.jpg" imagesrcset="/cdn-cgi/image/width=480,format=auto/https://files.realpython.com/media/Python-3.10-Cool-New-Features-for-You-to-Try_Watermarked.e2782d8a16dc.jpg 480w, /cdn-cgi/image/width=640,format=auto/https://files.realpython.com/media/Python-3.10-Cool-New-Features-for-You-to-Try_Watermarked.e2782d8a16dc.jpg 640w, /cdn-cgi/image/width=960,format=auto/https://files.realpython.com/media/Python-3.10-Cool-New-Features-for-You-to-Try_Watermarked.e2782d8a16dc.jpg 960w, /cdn-cgi/image/width=1920,format=auto/https://files.realpython.com/media/Python-3.10-Cool-New-Features-for-You-to-Try_Watermarked.e2782d8a16dc.jpg 1920w" imagesizes="(min-width: 1200px) 690px, (min-width: 780px) calc(-5vw + 669px), (min-width: 580px) 510px, calc(100vw - 30px)" />
|
||
<link rel="canonical" href="https://realpython.com/python310-new-features/" />
|
||
<meta name="twitter:card" content="summary_large_image" />
|
||
<meta name="twitter:image" content="https://files.realpython.com/media/Python-3.10-Cool-New-Features-for-You-to-Try_Watermarked.e2782d8a16dc.jpg" />
|
||
<meta property="og:image" content="https://files.realpython.com/media/Python-3.10-Cool-New-Features-for-You-to-Try_Watermarked.e2782d8a16dc.jpg" />
|
||
<meta name="twitter:creator" content="@realpython" />
|
||
<meta name="twitter:site" content="@realpython" />
|
||
<meta property="og:title" content="Python 3.10: Cool New Features for You to Try – Real Python" />
|
||
<meta property="og:type" content="article" />
|
||
<meta property="og:url" content="https://realpython.com/python310-new-features/" />
|
||
<meta property="og:description" content="In this tutorial, you'll explore some of the coolest and most useful features in Python 3.10. You'll appreciate more user-friendly error messages, learn about how you can handle complicated data structures with structural pattern matching, and explore new enhancements to Python's type system." />
|
||
<link href="/static/favicon.68cbf4197b0c.png" rel="icon" />
|
||
<link href="https://realpython.com/atom.xml" rel="alternate" title="Real Python" type="application/atom+xml" />
|
||
<link rel="manifest" href="/manifest.json" />
|
||
<script src="https://connect.facebook.net/signals/config/2220911568135371?v=2.9.142&r=stable&domain=realpython.com" async="async"></script>
|
||
<script async="async" src="https://connect.facebook.net/en_US/fbevents.js"></script>
|
||
<script type="text/javascript" async="async" src="https://www.googletagmanager.com/gtag/js?id=G-00HDMQ5TW1&cx=c&_slc=1"></script>
|
||
<script async="async" src="//www.google-analytics.com/analytics.js"></script>
|
||
<script id="icons-data" type="application/json">
|
||
<![CDATA[
|
||
{"iconsUrl": "/static/icons.55e8f03acfe3.svg"}
|
||
]]>
|
||
</script>
|
||
<script id="optins-data" type="application/json">
|
||
<![CDATA[
|
||
{"turnstileSiteKey": "0x4AAAAAAAQQUCLRXBi8NnUh"}
|
||
]]>
|
||
</script>
|
||
<script>
|
||
<![CDATA[
|
||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||
|
||
|
||
|
||
ga('create', 'UA-35184939-1', 'auto', {'allowLinker': true});
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
ga('set', {
|
||
dimension1: false,
|
||
dimension2: false
|
||
});
|
||
|
||
|
||
ga('send', 'pageview');
|
||
|
||
]]>
|
||
</script>
|
||
<script async="async" src="https://www.googletagmanager.com/gtag/js?id=G-L7L6L5F6Y6"></script>
|
||
<script>
|
||
<![CDATA[
|
||
window.dataLayer = window.dataLayer || [];
|
||
function gtag(){dataLayer.push(arguments);}
|
||
gtag('js', new Date());
|
||
gtag('config', 'G-L7L6L5F6Y6');
|
||
|
||
|
||
]]>
|
||
</script>
|
||
<meta http-equiv="origin-trial" content="AymqwRC7u88Y4JPvfIF2F37QKylC04248hLCdJAsh8xgOfe/dVJPV3XS3wLFca1ZMVOtnBfVjaCMTVudWM//5g4AAAB7eyJvcmlnaW4iOiJodHRwczovL3d3dy5nb29nbGV0YWdtYW5hZ2VyLmNvbTo0NDMiLCJmZWF0dXJlIjoiUHJpdmFjeVNhbmRib3hBZHNBUElzIiwiZXhwaXJ5IjoxNjk1MTY3OTk5LCJpc1RoaXJkUGFydHkiOnRydWV9" />
|
||
<meta http-equiv="origin-trial" content="AymqwRC7u88Y4JPvfIF2F37QKylC04248hLCdJAsh8xgOfe/dVJPV3XS3wLFca1ZMVOtnBfVjaCMTVudWM//5g4AAAB7eyJvcmlnaW4iOiJodHRwczovL3d3dy5nb29nbGV0YWdtYW5hZ2VyLmNvbTo0NDMiLCJmZWF0dXJlIjoiUHJpdmFjeVNhbmRib3hBZHNBUElzIiwiZXhwaXJ5IjoxNjk1MTY3OTk5LCJpc1RoaXJkUGFydHkiOnRydWV9" />
|
||
<style>
|
||
<![CDATA[
|
||
AD-SLOT,
|
||
AD-TRIPLE-BOX,
|
||
DFP-AD,
|
||
[class^="adDisplay-module"],
|
||
[class^="amp-ad-"],
|
||
[data-ad-cls],
|
||
[data-adbridg-ad-class],
|
||
[data-css-class="dfp-inarticle"],
|
||
[data-d-ad-id],
|
||
[data-desktop-ad-id],
|
||
[data-dynamic-ads],
|
||
[data-ez-name],
|
||
[data-freestar-ad],
|
||
[data-id^="div-gpt-ad"],
|
||
[data-m-ad-id],
|
||
[data-mobile-ad-id],
|
||
[data-name="adaptiveConstructorAd"],
|
||
[data-rc-widget],
|
||
[data-role="tile-ads-module"],
|
||
[data-template-type="nativead"],
|
||
[data-testid="adBanner-wrapper"],
|
||
[data-testid="ad_testID"],
|
||
[data-type="ad-vertical"],
|
||
[data-wpas-zoneid],
|
||
[id^="ad-wrap-"],
|
||
[id^="ad_sky"],
|
||
[id^="ad_slider"],
|
||
[id^="section-ad-banner"],
|
||
[onclick^="location.href='http://www.reimageplus.com"],
|
||
a-ad,
|
||
a[style="width:100%;height:100%;z-index:10000000000000000;position:absolute;top:0;left:0;"],
|
||
ad-shield-ads,
|
||
ad-slot,
|
||
app-ad,
|
||
app-advertisement,
|
||
app-large-ad,
|
||
ark-top-ad,
|
||
aside[id^="adrotate_widgets-"],
|
||
atf-ad-slot,
|
||
bottomadblock,
|
||
display-ads,
|
||
div[class^="Adstyled__AdWrapper-"],
|
||
div[class^="Display_displayAd"],
|
||
div[class^="kiwi-ad-wrapper"],
|
||
div[class^="native-ad-"],
|
||
div[data-ad-placeholder],
|
||
div[data-ad-wrapper],
|
||
div[data-adname],
|
||
div[data-adunit-path],
|
||
div[data-adunit],
|
||
div[data-adzone],
|
||
div[data-alias="300x250 Ad 1"],
|
||
div[data-alias="300x250 Ad 2"],
|
||
div[data-content="Advertisement"],
|
||
div[data-contentexchange-widget],
|
||
div[data-dfp-id],
|
||
div[data-id-advertdfpconf],
|
||
div[data-insertion],
|
||
div[data-mini-ad-unit],
|
||
div[data-native-ad],
|
||
div[id^="_vdo_ads_player_ai_"],
|
||
div[id^="ad-div-"],
|
||
div[id^="ad-position-"],
|
||
div[id^="adngin-"],
|
||
div[id^="adrotate_widgets-"],
|
||
div[id^="ads250_250-widget-"],
|
||
div[id^="ads300_100-widget-"],
|
||
div[id^="ads300_250-widget-"],
|
||
div[id^="adspot-"],
|
||
div[id^="advads_ad_"],
|
||
div[id^="crt-"][style],
|
||
div[id^="div-ads-"],
|
||
div[id^="google_dfp_"],
|
||
div[id^="gpt_ad_"],
|
||
div[id^="lazyad-"],
|
||
div[id^="optidigital-adslot"],
|
||
div[id^="pa_sticky_ad_box_middle_"],
|
||
div[id^="rc-widget-"],
|
||
div[id^="sticky_ad_"],
|
||
div[id^="vuukle-ad-"],
|
||
gpt-ad,
|
||
guj-ad,
|
||
hl-adsense,
|
||
img[src^="https://images.purevpnaffiliates.com"],
|
||
ps-connatix-module,
|
||
span[data-ez-ph-id],
|
||
span[id^="ezoic-pub-ad-placeholder-"],
|
||
topadblock,
|
||
zeus-ad,
|
||
[class^="tile-picker__CitrusBannerContainer-sc-"],
|
||
citrus-ad-wrapper,
|
||
[class^="s2nPlayer"],
|
||
div[id*="MarketGid"],
|
||
[onclick*="content.ad/"],
|
||
AMP-AD,
|
||
amp-ad,
|
||
amp-ad-custom,
|
||
amp-connatix-player,
|
||
amp-fx-flying-carpet,
|
||
a[data-obtrack^="http://paid.outbrain.com/network/redir?"],
|
||
a[data-oburl^="https://paid.outbrain.com/network/redir?"],
|
||
a[data-redirect^="https://paid.outbrain.com/network/redir?"],
|
||
a[data-url^="http://paid.outbrain.com/network/redir?"] + .author,
|
||
a[data-widget-outbrain-redirect^="http://paid.outbrain.com/network/redir?"],
|
||
a[onmousedown^="this.href='http://paid.outbrain.com/network/redir?"][target="_blank"],
|
||
a[onmousedown^="this.href='http://paid.outbrain.com/network/redir?"][target="_blank"] + .ob_source,
|
||
a[onmousedown^="this.href='https://paid.outbrain.com/network/redir?"][target="_blank"] + .ob_source,
|
||
a[target="_blank"][onmousedown="this.href^='http://paid.outbrain.com/network/redir?"],
|
||
div[id^="taboola-stream-"],
|
||
display-ad-component,
|
||
div[id^="zergnet-widget"],
|
||
a[aria-label="Werbelink"],
|
||
[onclick*="window.open('http://deloplen.com/"],
|
||
[data-uri^="https://s3.amazonaws.com"],
|
||
[data-lnguri^="https://s3.amazonaws.com"],
|
||
[onclick*="postlnk.com"],
|
||
[class^="DisplayAd"],
|
||
div[class*="displayAdRight"],
|
||
[data-lnguri*="vipbox"],
|
||
[class^="div-gpt-ad"]:not([style^="width: 1px; height: 1px; position: absolute; left: -10000px; top: -"]),
|
||
[id^="div-gpt-ad"]:not([style^="width: 1px; height: 1px; position: absolute; left: -10000px; top: -"]),
|
||
div[id^="div-gpt-"]:not([style^="width: 1px; height: 1px; position: absolute; left: -10000px; top: -"]),
|
||
.samBannerUnit, .samCodeUnit,
|
||
[src^="//dombnrs.com/"],
|
||
[src^="https://forum.picbaron.com/Banner"],
|
||
div[id][style^="position: fixed; inset: 0px; z-index: 2147483647; background: black"][style*="opacity: 0.01"],
|
||
div[class$="player-promo-col"],
|
||
div#spot-holder.spot-holder[style="display: block;"],
|
||
a[dontfo=""][style$="position: absolute; z-index: 2147483647;"],
|
||
[data-ad-module]:not([style$="left: -10000px !important; top: -1000px !important;"]):not(.adsbygoogle),
|
||
[data-ad-width]:not([style$="left: -10000px !important; top: -1000px !important;"]):not(.adsbygoogle),
|
||
[data-adblockkey]:not([style$="left: -10000px !important; top: -1000px !important;"]):not(html):not(.adsbygoogle),
|
||
[data-advadstrackid]:not([style$="left: -10000px !important; top: -1000px !important;"]):not(.adsbygoogle),
|
||
[data-ad-manager-id]:not([style$="left: -10000px !important; top: -1000px !important;"]):not(.adsbygoogle),
|
||
div[style^="z-index: 999999; background-image: url(\"data:image/gif;base64,"][style$="position: absolute;"],
|
||
a[onclick="openAuc();"],
|
||
div[style="position: fixed; display: block; width: 100%; height: 100%; inset: 0px; background-color: rgba(0, 0, 0, 0); z-index: 300000;"] { display: none !important; }
|
||
|
||
div[style^="display:block;position:relative;"],
|
||
[data-ad-name],
|
||
[data-revive-zoneid],
|
||
[id^="google_ads_iframe"],
|
||
[name^="google_ads_iframe"],
|
||
article.ad,
|
||
div[aria-label="Ads"],
|
||
div[data-ad-targeting],
|
||
div[data-google-query-id],
|
||
div[data-native_ad],
|
||
div[id^="ad_position_"],
|
||
div[id^="dfp-ad-"],
|
||
div[id^="ezoic-pub-ad-"],
|
||
div[id^="yandex_ad"],
|
||
ins.adsbygoogle,
|
||
div[id*="ScriptRoot"],
|
||
a[onmousedown^="this.href='https://paid.outbrain.com/network/redir?"][target="_blank"],
|
||
.trc_related_container div[data-item-syndicated="true"],
|
||
amp-embed[type="taboola"] { display: none !important; }
|
||
]]>
|
||
</style>
|
||
<style>
|
||
<![CDATA[
|
||
[href="https://t.me/Russia_Vs_Ukraine_War3"],
|
||
a[href^="https://www.vavoo.tv/promo/"] > img,
|
||
[href*="uselnk.com/"],
|
||
a[href^="https://bs.serving-sys.com"],
|
||
a[href^="https://join.virtuallust3d.com/"],
|
||
a[href^="http://adtrack"],
|
||
a[href^="https://www.mrskin.com/account/"],
|
||
[href^="https://rapidgator.net/article/premium/ref/"],
|
||
[href="https://ourgoldguy.com/contact/"] img,
|
||
a[href^="https://pb-front.com/"],
|
||
a[href^="http://www.eis.de/index.phtml?refid="],
|
||
[href*="postlnk.com"],
|
||
[href*="passtechusa.com"],
|
||
[href*="speenphorbin.com"] { display: none !important; }
|
||
]]>
|
||
</style>
|
||
<script src="https://cdn.onesignal.com/sdks/OneSignalPageSDKES6.js?v=151605" async="async"></script>
|
||
<link rel="stylesheet" href="https://onesignal.com/sdks/OneSignalSDKStyles.css?v=2" />
|
||
<script src="https://realpython.disqus.com/count-data.js?1=https%3A%2F%2Frealpython.com%2Fpython310-new-features%2F"></script>
|
||
</head>
|
||
<body>
|
||
<nav class="navbar fixed-top navbar-expand-lg navbar-dark flex-column">
|
||
<div class="container flex-row">
|
||
<a class="navbar-brand" href="/"><img src="/static/real-python-logo.893c30edea53.svg" width="165" height="40" class="d-inline-block align-top" alt="Real Python" /></a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPSczMCcgaGVpZ2h0PSczMCcgdmlld0JveD0nMCAwIDMwIDMwJz48cGF0aCBzdHJva2U9J3JnYmEoMjU1LCAyNTUsIDI1NSwgMC41KScgc3Ryb2tlLWxpbmVjYXA9J3JvdW5kJyBzdHJva2UtbWl0ZXJsaW1pdD0nMTAnIHN0cm9rZS13aWR0aD0nMicgZD0nTTQgN2gyMk00IDE1aDIyTTQgMjNoMjInLz48L3N2Zz4=" /></button>
|
||
<div class="collapse navbar-collapse navbar-nav-scroll" id="navbarSupportedContent" role="navigation" aria-label="Main Navigation">
|
||
<ul class="navbar-nav mr-2">
|
||
<li class="nav-item">
|
||
<a class="nav-link" href="/start-here/">Start Here</a>
|
||
</li>
|
||
<li class="nav-item dropdown">
|
||
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownLibrary" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#brands--python"></use></svg></span> Learn Python</a>
|
||
<div class="dropdown-menu" aria-labelledby="navbarDropdownLibrary">
|
||
<a class="dropdown-item" href="/search?kind=article&kind=course&order=newest" style="color: #ff7e73; line-height: 110%;"><span class="icon baseline mr-1"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#solid--graduation-cap"></use></svg></span>Python Tutorials →<br />
|
||
<small class="text-secondary">In-depth articles and video courses</small></a> <a class="dropdown-item" href="/learning-paths/" style="color: #ffc873; line-height: 110%;"><span class="icon baseline mr-1"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@learning-path"></use></svg></span>Learning Paths →<br />
|
||
<small class="text-secondary">Guided study plans for accelerated learning</small></a> <a class="dropdown-item" href="/quizzes/" style="color: #abe0e5; line-height: 110%;"><span class="icon baseline mr-1"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@quiz"></use></svg></span>Quizzes →<br />
|
||
<small class="text-secondary">Check your learning progress</small></a> <a class="dropdown-item" href="/tutorials/all/" style="color: #ccc; line-height: 110%;"><span class="icon baseline mr-1"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@category"></use></svg></span>Browse Topics →<br />
|
||
<small class="text-secondary">Focus on a specific area or skill level</small></a> <a class="dropdown-item" href="/community/" style="color: #e5c6ab; line-height: 110%;"><span class="icon baseline mr-1"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#solid--message-smile"></use></svg></span>Community Chat →<br />
|
||
<small class="text-secondary">Learn with other Pythonistas</small></a> <a class="dropdown-item" href="/office-hours/" style="color: #e5c6ab; line-height: 110%;"><span class="icon baseline mr-1"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#solid--users"></use></svg></span>Office Hours →<br />
|
||
<small class="text-secondary">Live Q&A calls with Python experts</small></a> <a class="dropdown-item" href="/podcasts/rpp/" style="color: #b8abe5; line-height: 110%;"><span class="icon baseline mr-1"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@podcast"></use></svg></span>Podcast →<br />
|
||
<small class="text-secondary">Hear what’s new in the world of Python</small></a> <a class="dropdown-item" href="/products/books/" style="color: #abe5b1; line-height: 110%;"><span class="icon baseline mr-1"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#solid--book-heart"></use></svg></span>Books →<br />
|
||
<small class="text-secondary">Round out your knowledge and learn offline</small></a> <a class="dropdown-item border-top text-warning" href="/account/join/" style="line-height: 110%;"><span class="icon baseline mr-1"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#solid--star"></use></svg></span>Unlock All Content →</a>
|
||
</div>
|
||
</li>
|
||
<li class="nav-item dropdown">
|
||
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMore" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">More</a>
|
||
<div class="dropdown-menu" aria-labelledby="navbarDropdownMore">
|
||
<a class="dropdown-item" href="/learner-stories/">Learner Stories</a> <a class="dropdown-item" href="/newsletter/">Python Newsletter</a> <a class="dropdown-item" href="https://www.pythonjobshq.com" target="_blank">Python Job Board</a> <a class="dropdown-item" href="/team/">Meet the Team</a> <a class="dropdown-item" href="/write-for-us/">Become a Tutorial Writer</a> <a class="dropdown-item" href="/become-an-instructor/">Become a Video Instructor</a>
|
||
</div>
|
||
</li>
|
||
</ul>
|
||
<div class="d-block d-lg-none">
|
||
<ul class="navbar-nav">
|
||
<li class="nav-item">
|
||
<a class="nav-link" href="/search" title="Search"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#v4--search"></use></svg></span> Search</a>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
<div class="d-none d-lg-flex align-items-center mr-2 flex-fill">
|
||
<form class="form-inline w-100 position-relative" action="/search" method="get">
|
||
<a class="js-search-form-submit position-absolute" href="/search" title="Search"><span class="icon baseline text-muted pl-2"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#v4--search"></use></svg></span></a> <input class="search-autocomplete search-field form-control form-control-md mr-sm-1 mr-lg-2 w-100" style="padding-left: 2rem;" maxlength="50" type="search" placeholder="Search" aria-label="Search" aria-keyshortcuts="/ Control+J Meta+J" name="q" autocomplete="off" /> <span class="search-hotkey-indicator position-absolute px-2 border border-input text-input rounded small user-select-none" style="right: 1em;" title="Press / (forward slash) or Ctrl+J to open search" aria-hidden="true">/</span> <input type="hidden" name="_from" value="nav" />
|
||
</form>
|
||
</div>
|
||
<ul class="navbar-nav ml-auto">
|
||
<li class="nav-item form-inline">
|
||
<a class="ml-2 ml-lg-0 btn btn-sm btn-primary px-3" href="/account/join/">Join</a>
|
||
</li>
|
||
<li class="nav-item">
|
||
<a class="btn text-light" href="/account/login/?next=%2Fpython310-new-features%2F">Sign‑In</a>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</nav>
|
||
<div class="container main-content">
|
||
<div class="row justify-content-center">
|
||
<aside class="col-md-7 col-lg-4 order-2 d-none d-lg-block">
|
||
<div class="card mb-3 bg-secondary">
|
||
<form class="card-body" action="/optins/process/" method="post">
|
||
<div class="form-group">
|
||
<p class="h5 text-muted text-center">
|
||
— FREE Email Series —
|
||
</p>
|
||
<p class="h3 text-center">
|
||
🐍 Python Tricks 💌
|
||
</p>
|
||
<p>
|
||
<img loading="lazy" class="img-fluid rounded" src="/static/pytrick-dict-merge.4201a0125a5e.png" width="738" height="490" alt="Python Tricks Dictionary Merge" />
|
||
</p>
|
||
</div>
|
||
<div class="form-group">
|
||
<input type="hidden" name="csrfmiddlewaretoken" value="uS9jW4J1OA6NsjWe9e3lAb0KuP7EYOUAGX3WH46iyCnwy1uBqPKTipw66s5mt7Yj" /> <input type="hidden" name="slug" value="static-python-tricks-sidebar" /> <input type="email" class="form-control form-control-md" name="email" placeholder="Email…" required="" />
|
||
</div><button type="submit" name="submit" class="btn btn-primary btn-md btn-block">Get Python Tricks »</button>
|
||
<p class="mb-0 mt-2 text-muted text-center">
|
||
🔒 No spam. Unsubscribe any time.
|
||
</p>
|
||
</form>
|
||
</div>
|
||
<div class="sidebar-module sidebar-module-inset border">
|
||
<a href="/tutorials/all/" class="badge badge-light text-muted"><span class="icon baseline mr-1"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@category"></use></svg></span>Browse Topics</a> <a href="/learning-paths/" class="badge badge-light text-muted"><span class="icon baseline mr-1"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#solid--map-location-dot"></use></svg></span>Guided Learning Paths</a><br />
|
||
<a href="/search?level=basics" class="badge badge-light text-muted"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#v4--angle-double-up"></use></svg></span> Basics</a> <a href="/search?level=intermediate" class="badge badge-light text-muted"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#v4--angle-double-up"></use></svg></span><span class="icon baseline tight-left"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#v4--angle-double-up"></use></svg></span> Intermediate</a> <a href="/search?level=advanced" class="badge badge-light text-muted"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#v4--angle-double-up"></use></svg></span><span class="icon baseline tight-left"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#v4--angle-double-up"></use></svg></span><span class="icon baseline tight-left"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#v4--angle-double-up"></use></svg></span> Advanced</a>
|
||
<hr class="my-2" />
|
||
<a href="/tutorials/api/" class="badge badge-light text-muted">api</a> <a href="/tutorials/best-practices/" class="badge badge-light text-muted">best-practices</a> <a href="/tutorials/career/" class="badge badge-light text-muted">career</a> <a href="/tutorials/community/" class="badge badge-light text-muted">community</a> <a href="/tutorials/databases/" class="badge badge-light text-muted">databases</a> <a href="/tutorials/data-science/" class="badge badge-light text-muted">data-science</a> <a href="/tutorials/data-structures/" class="badge badge-light text-muted">data-structures</a> <a href="/tutorials/data-viz/" class="badge badge-light text-muted">data-viz</a> <a href="/tutorials/devops/" class="badge badge-light text-muted">devops</a> <a href="/tutorials/django/" class="badge badge-light text-muted">django</a> <a href="/tutorials/docker/" class="badge badge-light text-muted">docker</a> <a href="/tutorials/editors/" class="badge badge-light text-muted">editors</a> <a href="/tutorials/flask/" class="badge badge-light text-muted">flask</a> <a href="/tutorials/front-end/" class="badge badge-light text-muted">front-end</a> <a href="/tutorials/gamedev/" class="badge badge-light text-muted">gamedev</a> <a href="/tutorials/gui/" class="badge badge-light text-muted">gui</a> <a href="/tutorials/machine-learning/" class="badge badge-light text-muted">machine-learning</a> <a href="/tutorials/numpy/" class="badge badge-light text-muted">numpy</a> <a href="/tutorials/projects/" class="badge badge-light text-muted">projects</a> <a href="/tutorials/python/" class="badge badge-light text-muted">python</a> <a href="/tutorials/testing/" class="badge badge-light text-muted">testing</a> <a href="/tutorials/tools/" class="badge badge-light text-muted">tools</a> <a href="/tutorials/web-dev/" class="badge badge-light text-muted">web-dev</a> <a href="/tutorials/web-scraping/" class="badge badge-light text-muted">web-scraping</a>
|
||
</div>
|
||
<div class="sidebar-module sidebar-module-inset p-0" style="overflow:hidden;">
|
||
<div style="display:block;position:relative;">
|
||
<div style="display:block;width:100%;padding-top:100%;"></div>
|
||
<div class="rpad" data-unit="1x1" style="position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;">
|
||
<a href="https://srv.realpython.net/click/67941047673/?c=48874902176&p=58946116052&r=79260" rel="nofollow" target="_blank"><img src="https://img.realpython.net/996f1dd97a6e6bd3a43f224110634f22" style="max-width: 100%; max-height: 100%; width: 100%;" /></a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sidebar-sticky">
|
||
<div class="bg-light sidebar-module sidebar-module-inset" id="sidebar-toc">
|
||
<p class="h4 text-muted">
|
||
<a class="link-unstyled" href="#toc">Table of Contents</a>
|
||
</p>
|
||
<div class="toc">
|
||
<ul>
|
||
<li class="">
|
||
<a href="#better-error-messages">Better Error Messages</a>
|
||
</li>
|
||
<li class="">
|
||
<a href="#structural-pattern-matching">Structural Pattern Matching</a>
|
||
<ul>
|
||
<li>
|
||
<a href="#deconstructing-data-structures">Deconstructing Data Structures</a>
|
||
</li>
|
||
<li>
|
||
<a href="#using-different-kinds-of-patterns">Using Different Kinds of Patterns</a>
|
||
</li>
|
||
<li>
|
||
<a href="#matching-literal-patterns">Matching Literal Patterns</a>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li class="">
|
||
<a href="#type-unions-aliases-and-guards">Type Unions, Aliases, and Guards</a>
|
||
</li>
|
||
<li class="">
|
||
<a href="#stricter-zipping-of-sequences">Stricter Zipping of Sequences</a>
|
||
</li>
|
||
<li class="">
|
||
<a href="#new-functions-in-the-statistics-module">New Functions in the statistics Module</a>
|
||
</li>
|
||
<li class="">
|
||
<a href="#other-pretty-cool-features">Other Pretty Cool Features</a>
|
||
<ul>
|
||
<li>
|
||
<a href="#default-text-encodings">Default Text Encodings</a>
|
||
</li>
|
||
<li>
|
||
<a href="#asynchronous-iteration">Asynchronous Iteration</a>
|
||
</li>
|
||
<li>
|
||
<a href="#context-manager-syntax">Context Manager Syntax</a>
|
||
</li>
|
||
<li>
|
||
<a href="#modern-and-secure-ssl">Modern and Secure SSL</a>
|
||
</li>
|
||
<li>
|
||
<a href="#more-information-about-your-python-interpreter">More Information About Your Python Interpreter</a>
|
||
</li>
|
||
<li>
|
||
<a href="#future-annotations">Future Annotations</a>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li class="">
|
||
<a href="#how-to-detect-python-310-at-runtime">How to Detect Python 3.10 at Runtime</a>
|
||
</li>
|
||
<li class="">
|
||
<a href="#so-should-you-upgrade-to-python-310">So, Should You Upgrade to Python 3.10?</a>
|
||
</li>
|
||
<li class="">
|
||
<a href="#conclusion">Conclusion</a>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="sidebar-module sidebar-module-inset text-center my-3 py-0">
|
||
<div class="jsCompletionStatusWidget btn-group mb-0">
|
||
<button title="Click to mark as completed" class="jsBtnCompletion btn btn-secondary border-right" style="border-top-right-radius: 0; border-bottom-right-radius: 0;">Mark as Completed</button> <button title="Add bookmark" class="jsBtnBookmark btn btn-secondary border-left"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#light--bookmark"></use></svg></span></button>
|
||
</div>
|
||
<div class="my-2">
|
||
<div class="btn-group mb-0">
|
||
<a class="btn btn-secondary border-right" style="border-top-right-radius: 0; border-bottom-right-radius: 0;" title="Liked it" role="button" aria-label="Thumbs up (liked it)" href="/feedback/survey/article/python310-new-features/liked/?from=article-sidebar" target="_blank"><span class="icon"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#light--thumbs-up"></use></svg></span></a> <a class="btn btn-secondary border-left" role="button" aria-label="Thumbs down (disliked it)" title="Disliked it" href="/feedback/survey/article/python310-new-features/disliked/?from=article-sidebar" target="_blank"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#light--thumbs-down"></use></svg></span></a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sidebar-module sidebar-module-inset text-center my-3 py-0">
|
||
<span><a target="_blank" rel="nofollow" href="https://twitter.com/intent/tweet/?text=Check%20out%20this%20%23Python%20tutorial:%20Python%203.10%3A%20Cool%20New%20Features%20for%20You%20to%20Try%20by%20@realpython&url=https%3A//realpython.com/python310-new-features/" class="mr-1 badge badge-x-twitter text-light mb-1"><span class="icon baseline mr-1 text-light"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#brands--x-twitter"></use></svg></span>Share</a> <a target="_blank" rel="nofollow" href="https://facebook.com/sharer/sharer.php?u=https%3A//realpython.com/python310-new-features/" class="mr-1 badge badge-facebook text-light mb-1"><span class="icon baseline mr-1 text-light"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#brands--facebook"></use></svg></span>Share</a> <a target="_blank" rel="nofollow" href="mailto:?subject=Python%20article%20for%20you&body=Check%20out%20this%20Python%20tutorial:%0A%0APython%203.10%3A%20Cool%20New%20Features%20for%20You%20to%20Try%0A%0Ahttps%3A//realpython.com/python310-new-features/" class="badge badge-red text-light mb-1"><span class="icon baseline mr-1 text-light"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#solid--envelope"></use></svg></span>Email</a></span>
|
||
</div>
|
||
<div class="sidebar-module sidebar-module-inset border card">
|
||
<p>
|
||
<span class="badge badge-pill badge-success"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#v4--play-circle"></use></svg></span> Recommended Video Course</span><br />
|
||
<a class="stretched-link text-success" href="/courses/cool-new-features-python-310/">Cool New Features in Python 3.10</a>
|
||
</p>
|
||
</div>
|
||
<div class="sidebar-module sidebar-module-inset p-0" style="overflow:hidden;">
|
||
<div style="display:block;position:relative;">
|
||
<div style="display:block;width:100%;padding-top:25%;"></div>
|
||
<div class="rpad" data-unit="4x1" style="position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;">
|
||
<a href="https://srv.realpython.net/click/63154477675/?c=48874902176&p=58946116052&r=99799" rel="nofollow" target="_blank"><img src="https://img.realpython.net/2715fa5362672de6ffb80b688c08675b" style="max-width: 100%; max-height: 100%; width: 100%;" /></a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</aside>
|
||
<div class="col-md-11 col-lg-8 article with-headerlinks">
|
||
<figure class="embed-responsive embed-responsive-16by9 rounded">
|
||
<img class="card-img-top m-0 p-0 embed-responsive-item" style="object-fit: contain; background: #e5c5ac;" alt="Python 3.10: Cool New Features for You to Try" width="1920" height="1080" src="https://files.realpython.com/media/Python-3.10-Cool-New-Features-for-You-to-Try_Watermarked.e2782d8a16dc.jpg" srcset="/cdn-cgi/image/width=480,format=auto/https://files.realpython.com/media/Python-3.10-Cool-New-Features-for-You-to-Try_Watermarked.e2782d8a16dc.jpg 480w, /cdn-cgi/image/width=640,format=auto/https://files.realpython.com/media/Python-3.10-Cool-New-Features-for-You-to-Try_Watermarked.e2782d8a16dc.jpg 640w, /cdn-cgi/image/width=960,format=auto/https://files.realpython.com/media/Python-3.10-Cool-New-Features-for-You-to-Try_Watermarked.e2782d8a16dc.jpg 960w, /cdn-cgi/image/width=1920,format=auto/https://files.realpython.com/media/Python-3.10-Cool-New-Features-for-You-to-Try_Watermarked.e2782d8a16dc.jpg 1920w" sizes="(min-width: 1200px) 690px, (min-width: 780px) calc(-5vw + 669px), (min-width: 580px) 510px, calc(100vw - 30px)" fetchpriority="high" />
|
||
</figure>
|
||
<h1>
|
||
Python 3.10: Cool New Features for You to Try
|
||
</h1>
|
||
<div class="mb-0">
|
||
<span class="text-muted">by <a class="text-muted" href="#author">Geir Arne Hjelle</a> <span class="icon baseline ml-2 mr-1"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#solid--comments"></use></svg></span><a class="text-muted" href="#reader-comments"><span class="disqus-comment-count" data-disqus-identifier="https://realpython.com/python310-new-features/">2 Comments</span></a> <span class="d-inline d-md-block"><span class="icon baseline ml-2 ml-md-0"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@category"></use></svg></span> <a href="/tutorials/intermediate/" class="badge badge-light text-muted">intermediate</a> <a href="/tutorials/python/" class="badge badge-light text-muted">python</a></span></span>
|
||
<div class="d-sm-flex flex-row justify-content-between my-3 text-center">
|
||
<div class="jsCompletionStatusWidget btn-group mb-0">
|
||
<button title="Click to mark as completed" class="jsBtnCompletion btn btn-secondary border-right" style="border-top-right-radius: 0; border-bottom-right-radius: 0;">Mark as Completed</button> <button title="Add bookmark" class="jsBtnBookmark btn btn-secondary border-left"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#light--bookmark"></use></svg></span></button>
|
||
</div>
|
||
<div class="align-self-center my-2">
|
||
<span><a target="_blank" rel="nofollow" href="https://twitter.com/intent/tweet/?text=Check%20out%20this%20%23Python%20tutorial:%20Python%203.10%3A%20Cool%20New%20Features%20for%20You%20to%20Try%20by%20@realpython&url=https%3A//realpython.com/python310-new-features/" class="mr-1 badge badge-x-twitter text-light mb-1"><span class="icon baseline mr-1 text-light"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#brands--x-twitter"></use></svg></span>Share</a> <a target="_blank" rel="nofollow" href="https://facebook.com/sharer/sharer.php?u=https%3A//realpython.com/python310-new-features/" class="mr-1 badge badge-facebook text-light mb-1"><span class="icon baseline mr-1 text-light"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#brands--facebook"></use></svg></span>Share</a> <a target="_blank" rel="nofollow" href="mailto:?subject=Python%20article%20for%20you&body=Check%20out%20this%20Python%20tutorial:%0A%0APython%203.10%3A%20Cool%20New%20Features%20for%20You%20to%20Try%0A%0Ahttps%3A//realpython.com/python310-new-features/" class="badge badge-red text-light mb-1"><span class="icon baseline mr-1 text-light"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#solid--envelope"></use></svg></span>Email</a></span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="article-body">
|
||
<div class="bg-light sidebar-module sidebar-module-inset" id="toc">
|
||
<p class="h3 mb-2 text-muted">
|
||
Table of Contents
|
||
</p>
|
||
<div class="toc">
|
||
<ul>
|
||
<li>
|
||
<a href="#better-error-messages">Better Error Messages</a>
|
||
</li>
|
||
<li>
|
||
<a href="#structural-pattern-matching">Structural Pattern Matching</a>
|
||
<ul>
|
||
<li>
|
||
<a href="#deconstructing-data-structures">Deconstructing Data Structures</a>
|
||
</li>
|
||
<li>
|
||
<a href="#using-different-kinds-of-patterns">Using Different Kinds of Patterns</a>
|
||
</li>
|
||
<li>
|
||
<a href="#matching-literal-patterns">Matching Literal Patterns</a>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<a href="#type-unions-aliases-and-guards">Type Unions, Aliases, and Guards</a>
|
||
</li>
|
||
<li>
|
||
<a href="#stricter-zipping-of-sequences">Stricter Zipping of Sequences</a>
|
||
</li>
|
||
<li>
|
||
<a href="#new-functions-in-the-statistics-module">New Functions in the statistics Module</a>
|
||
</li>
|
||
<li>
|
||
<a href="#other-pretty-cool-features">Other Pretty Cool Features</a>
|
||
<ul>
|
||
<li>
|
||
<a href="#default-text-encodings">Default Text Encodings</a>
|
||
</li>
|
||
<li>
|
||
<a href="#asynchronous-iteration">Asynchronous Iteration</a>
|
||
</li>
|
||
<li>
|
||
<a href="#context-manager-syntax">Context Manager Syntax</a>
|
||
</li>
|
||
<li>
|
||
<a href="#modern-and-secure-ssl">Modern and Secure SSL</a>
|
||
</li>
|
||
<li>
|
||
<a href="#more-information-about-your-python-interpreter">More Information About Your Python Interpreter</a>
|
||
</li>
|
||
<li>
|
||
<a href="#future-annotations">Future Annotations</a>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<a href="#how-to-detect-python-310-at-runtime">How to Detect Python 3.10 at Runtime</a>
|
||
</li>
|
||
<li>
|
||
<a href="#so-should-you-upgrade-to-python-310">So, Should You Upgrade to Python 3.10?</a>
|
||
</li>
|
||
<li>
|
||
<a href="#conclusion">Conclusion</a>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="sidebar-module sidebar-module-inset p-0" style="overflow:hidden;">
|
||
<div style="display:block;position:relative;">
|
||
<div style="display:block;width:100%;padding-top:12.5%;"></div>
|
||
<div class="rpad rounded border" data-unit="8x1" style="position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;">
|
||
<a href="https://srv.realpython.net/click/32532819624/?c=48874902176&p=58946116052&r=07122" rel="nofollow" target="_blank"><img src="https://img.realpython.net/6a663e90d4c9cf96e26ca6a22741fd28" style="max-width: 100%; max-height: 100%; width: 100%;" /></a>
|
||
</div>
|
||
</div><a class="small text-muted" href="/account/join/" rel="nofollow"><span class="icon baseline mr-1"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#solid--circle-info"></use></svg></span>Remove ads</a>
|
||
</div>
|
||
<div class="border rounded p-3 card mb-2">
|
||
<p class="mb-0">
|
||
<span class="badge badge-pill badge-success"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#v4--play-circle"></use></svg></span> Watch Now</span> This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: <a class="stretched-link text-success" href="/courses/cool-new-features-python-310/"><strong>Cool New Features in Python 3.10</strong></a>
|
||
</p>
|
||
</div>
|
||
<p>
|
||
<a href="https://www.python.org/downloads/release/python-3100/">Python 3.10 is out!</a> Volunteers have been working on the new version since May 2020 to bring you a better, faster, and more secure Python. As of <a href="https://www.python.org/dev/peps/pep-0619/">October 4, 2021</a>, the first official version is available.
|
||
</p>
|
||
<p>
|
||
Each new version of Python brings a host of changes. You can read about all of them in the <a href="https://docs.python.org/3.10/whatsnew/3.10.html">documentation</a>. Here, you’ll get to learn about the coolest new features.
|
||
</p>
|
||
<p>
|
||
<strong>In this tutorial, you’ll learn about:</strong>
|
||
</p>
|
||
<ul>
|
||
<li>Debugging with more helpful and precise <strong>error messages</strong>
|
||
</li>
|
||
<li>Using <strong>structural pattern matching</strong> to work with data structures
|
||
</li>
|
||
<li>Adding more readable and more specific <strong>type hints</strong>
|
||
</li>
|
||
<li>Checking the <strong>length of sequences</strong> when using <code>zip()</code>
|
||
</li>
|
||
<li>Calculating <strong>multivariable statistics</strong>
|
||
</li>
|
||
</ul>
|
||
<p>
|
||
To try out the new features yourself, you need to run Python 3.10. You can get it from the <a href="https://www.python.org/downloads/">Python homepage</a>. Alternatively, you can <a href="https://realpython.com/python-versions-docker/">use Docker</a> with the <a href="https://hub.docker.com/_/python/">latest Python image</a>.
|
||
</p>
|
||
<div class="alert alert-warning" role="alert">
|
||
<p>
|
||
<strong markdown="1">Free Bonus:</strong> <a href="https://realpython.com/bonus/python-mastery-course/" class="alert-link" data-toggle="modal" data-target="#modal-python-mastery-course" data-focus="false" markdown="1">5 Thoughts On Python Mastery</a>, a free course for Python developers that shows you the roadmap and the mindset you’ll need to take your Python skills to the next level.
|
||
</p>
|
||
</div>
|
||
<div class="alert alert-primary" role="alert">
|
||
<p>
|
||
<strong>Bonus Learning Materials:</strong> Check out <a href="https://realpython.com/podcasts/rpp/81/">Real Python Podcast Episode #81</a> for Python 3.10 tips and a discussion with members of the <em>Real Python</em> team.
|
||
</p>
|
||
</div>
|
||
<section class="section2">
|
||
<h2 id="better-error-messages">
|
||
Better Error Messages<a class="headerlink" href="#better-error-messages" title="Permanent link"></a>
|
||
</h2>
|
||
<p>
|
||
Python is often lauded for being a user-friendly programming language. While this is true, there are certain parts of Python that could be friendlier. Python 3.10 comes with a host of more precise and constructive error messages. In this section, you’ll see some of the newest improvements. The full list is available in the <a href="https://docs.python.org/3.10/whatsnew/3.10.html#better-error-messages">documentation</a>.
|
||
</p>
|
||
<p>
|
||
Think back to writing your first <a href="https://www.scriptol.com/programming/hello-world.php">Hello World</a> program in Python:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="c1"># hello.py</span>
|
||
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"Hello, World!)</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
Maybe you created a file, added the famous call to <code>print()</code>, and saved it as <code>hello.py</code>. You then ran the program, eager to call yourself a proper Pythonista. However, something went wrong:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="console" data-is-repl="true">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--yellow">
|
||
<span class="mr-2 noselect" aria-label="Language">Shell</span>
|
||
<div class="noselect">
|
||
<span class="codeblock__output-toggle" title="Toggle prompts and output"><span class="icon baseline js-codeblock-output-on codeblock__header--icon-lower"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#regular--rectangle-terminal"></use></svg></span></span>
|
||
</div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="gp">$ </span>python<span class="w"> </span>hello.py
|
||
<span class="go"> File "/home/rp/hello.py", line 3</span>
|
||
<span class="go"> print("Hello, World!)</span>
|
||
<span class="go"> ^</span>
|
||
<span class="go">SyntaxError: EOL while scanning string literal</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
There was a <code>SyntaxError</code> in the code. <code>EOL</code>, what does that even mean? You went back to your code, and after a bit of staring and searching, you realized that there was a missing quotation mark at the end of your string.
|
||
</p>
|
||
<p>
|
||
One of the more impactful improvements in Python 3.10 is better and more precise error messages for many common issues. If you run your buggy Hello World in Python 3.10, you’ll get a bit more help than in earlier versions of Python:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="console" data-is-repl="true">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--yellow">
|
||
<span class="mr-2 noselect" aria-label="Language">Shell</span>
|
||
<div class="noselect">
|
||
<span class="codeblock__output-toggle" title="Toggle prompts and output"><span class="icon baseline js-codeblock-output-on codeblock__header--icon-lower"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#regular--rectangle-terminal"></use></svg></span></span>
|
||
</div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="gp">$ </span>python<span class="w"> </span>hello.py
|
||
<span class="go"> File "/home/rp/hello.py", line 3</span>
|
||
<span class="go"> print("Hello, World!)</span>
|
||
<span class="go"> ^</span>
|
||
<span class="go">SyntaxError: unterminated string literal (detected at line 3)</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
The error message is still a bit technical, but gone is the mysterious <code>EOL</code>. Instead, the message tells you that you need to terminate your string! There are similar improvements to many different error messages, as you’ll see below.
|
||
</p>
|
||
<p>
|
||
A <a href="https://realpython.com/invalid-syntax-python/"><code>SyntaxError</code></a> is an error raised when your code is parsed, before it even starts to execute. Syntax errors can be tricky to debug because the interpreter provides imprecise or sometimes even misleading error messages. The following code is missing a curly brace to terminate the dictionary:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="linenos"> 1</span><span class="c1"># unterminated_dict.py</span>
|
||
<span class="linenos"> 2</span>
|
||
<span class="linenos"> 3</span><span class="n">months</span> <span class="o">=</span> <span class="p">{</span>
|
||
<span class="linenos"> 4</span> <span class="mi">10</span><span class="p">:</span> <span class="s2">"October"</span><span class="p">,</span>
|
||
<span class="linenos"> 5</span> <span class="mi">11</span><span class="p">:</span> <span class="s2">"November"</span><span class="p">,</span>
|
||
<span class="linenos"> 6</span> <span class="mi">12</span><span class="p">:</span> <span class="s2">"December"</span>
|
||
<span class="linenos"> 7</span>
|
||
<span class="linenos"> 8</span><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">months</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span><span class="si">}</span><span class="s2"> is the tenth month"</span><span class="p">)</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
The missing closing curly brace that should have been on line 7 is an error. If you run this code with Python 3.9 or earlier, you’ll see the following error message:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="pytb">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python Traceback</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code> File <span class="nb">"/home/rp/unterminated_dict.py"</span>, line <span class="m">8</span>
|
||
<span class="w"> </span><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">months</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span><span class="si">}</span><span class="s2"> is the tenth month"</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="pm">^</span>
|
||
<span class="gr">SyntaxError</span>: <span class="n">invalid syntax</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
The error message highlights line 8, but there are no syntactical problems in line 8! If you’ve experienced your share of syntax errors in Python, you might already know that the trick is to look at the lines <em>before</em> the one Python complains about. In this case, you’re looking for the missing closing brace on line 7.
|
||
</p>
|
||
<p>
|
||
In Python 3.10, the same code shows a much more helpful and precise error message:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="pytb">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python Traceback</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code> File <span class="nb">"/home/rp/unterminated_dict.py"</span>, line <span class="m">3</span>
|
||
<span class="w"> </span><span class="n">months</span> <span class="o">=</span> <span class="p">{</span>
|
||
<span class="w"> </span><span class="pm">^</span>
|
||
<span class="gr">SyntaxError</span>: <span class="n">'{' was never closed</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
This points you straight to the offending dictionary and allows you to fix the issue in no time.
|
||
</p>
|
||
<p>
|
||
There are a few other ways to mess up dictionary syntax. A typical one is forgetting a comma after one of the items:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="linenos"> 1</span><span class="c1"># missing_comma.py</span>
|
||
<span class="linenos"> 2</span>
|
||
<span class="linenos"> 3</span><span class="n">months</span> <span class="o">=</span> <span class="p">{</span>
|
||
<span class="hll"><span class="linenos"> 4</span> <span class="mi">10</span><span class="p">:</span> <span class="s2">"October"</span>
|
||
</span><span class="linenos"> 5</span> <span class="mi">11</span><span class="p">:</span> <span class="s2">"November"</span><span class="p">,</span>
|
||
<span class="linenos"> 6</span> <span class="mi">12</span><span class="p">:</span> <span class="s2">"December"</span><span class="p">,</span>
|
||
<span class="linenos"> 7</span><span class="p">}</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
In this code, a comma is missing at the end of line 4. Python 3.10 gives you a clear suggestion on how to fix your code:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="pytb">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python Traceback</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code> File <span class="nb">"/home/real_python/missing_comma.py"</span>, line <span class="m">4</span>
|
||
<span class="w"> </span><span class="mi">10</span><span class="p">:</span> <span class="s2">"October"</span>
|
||
<span class="w"> </span><span class="pm">^^^^^^^^^</span>
|
||
<span class="gr">SyntaxError</span>: <span class="n">invalid syntax. Perhaps you forgot a comma?</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
You can add the missing comma and have your code back up and running in no time.
|
||
</p>
|
||
<p>
|
||
Another common mistake is using the <a href="https://realpython.com/python-assignment-operator/">assignment operator</a> (<code>=</code>) instead of the equality comparison operator (<code>==</code>) when you’re comparing values. Previously, this would just cause another <code>invalid syntax</code> message. In the newest version of Python, you get some more advice:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="pycon" data-is-repl="true">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect">
|
||
<span class="codeblock__output-toggle" title="Toggle prompts and output"><span class="icon baseline js-codeblock-output-on codeblock__header--icon-lower"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#regular--rectangle-terminal"></use></svg></span></span>
|
||
</div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="gp">>>> </span><span class="k">if</span> <span class="n">month</span> <span class="o">=</span> <span class="s2">"October"</span><span class="p">:</span>
|
||
File <span class="nb">"<stdin>"</span>, line <span class="m">1</span>
|
||
<span class="w"> </span><span class="k">if</span> <span class="n">month</span> <span class="o">=</span> <span class="s2">"October"</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="pm">^^^^^^^^^^^^^^^^^</span>
|
||
<span class="gr">SyntaxError</span>: <span class="n">invalid syntax. Maybe you meant '==' or ':=' instead of '='?</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
The parser suggests that you maybe meant to use a <a href="https://realpython.com/python-operators-expressions/#comparison-operators">comparison operator</a> or an <a href="https://realpython.com/python-walrus-operator/">assignment expression operator</a> instead.
|
||
</p>
|
||
<p>
|
||
Take note of another nifty improvement in Python 3.10 error messages. The last two examples show how carets (<code>^^^</code>) highlight the whole offending expression. Previously, a single caret symbol (<code>^</code>) indicated just an approximate location.
|
||
</p>
|
||
<p>
|
||
The final error message improvement that you’ll play with for now is that attribute and name errors can now offer suggestions if you misspell an attribute or a name:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="pycon" data-is-repl="true">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect">
|
||
<span class="codeblock__output-toggle" title="Toggle prompts and output"><span class="icon baseline js-codeblock-output-on codeblock__header--icon-lower"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#regular--rectangle-terminal"></use></svg></span></span>
|
||
</div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="gp">>>> </span><span class="kn">import</span> <span class="nn">math</span>
|
||
<span class="gp">>>> </span><span class="n">math</span><span class="o">.</span><span class="n">py</span>
|
||
<span class="hll"><span class="go">AttributeError: module 'math' has no attribute 'py'. Did you mean: 'pi'?</span>
|
||
</span>
|
||
<span class="gp">>>> </span><span class="n">pint</span>
|
||
<span class="hll"><span class="go">NameError: name 'pint' is not defined. Did you mean: 'print'?</span>
|
||
</span>
|
||
<span class="gp">>>> </span><span class="n">release</span> <span class="o">=</span> <span class="s2">"3.10"</span>
|
||
<span class="gp">>>> </span><span class="n">relaese</span>
|
||
<span class="hll"><span class="go">NameError: name 'relaese' is not defined. Did you mean: 'release'?</span>
|
||
</span></code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
Note that the suggestions work for both built-in names and names that you define yourself, although they may <a href="https://docs.python.org/3.10/whatsnew/3.10.html#attributeerrors">not be available</a> in all environments. If you like these kinds of suggestions, check out <a href="https://github.com/SylvainDe/DidYouMean-Python">BetterErrorMessages</a>, which offers similar suggestions in even more contexts.
|
||
</p>
|
||
<p>
|
||
The improvements you’ve seen in this section are just some of the <a href="https://docs.python.org/3.10/whatsnew/3.10.html#better-error-messages">many error messages</a> that have gotten a face-lift. The new Python will be even more user-friendly than before, and hopefully, the new error messages will save you both time and frustration going forward.
|
||
</p>
|
||
<div>
|
||
<div class="rounded border border-light" style="display:block;position:relative;">
|
||
<div style="display:block;width:100%;padding-top:12.5%;"></div>
|
||
<div class="rpad rounded border" data-unit="8x1" style="position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;">
|
||
<a href="https://srv.realpython.net/click/16642766600/?c=48874902176&p=58946116052&r=80779" rel="nofollow" target="_blank"><img src="https://img.realpython.net/f1ef724573f8d74cc59cd07ea9e20a6b" style="max-width: 100%; max-height: 100%; width: 100%;" /></a>
|
||
</div>
|
||
</div><a class="small text-muted" href="/account/join/" rel="nofollow"><span class="icon baseline mr-1"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#solid--circle-info"></use></svg></span>Remove ads</a>
|
||
</div>
|
||
</section>
|
||
<section class="section2">
|
||
<h2 id="structural-pattern-matching">
|
||
Structural Pattern Matching<a class="headerlink" href="#structural-pattern-matching" title="Permanent link"></a>
|
||
</h2>
|
||
<p>
|
||
The biggest new feature in Python 3.10, probably both in terms of <a href="https://lwn.net/Articles/845480/">controversy</a> and <a href="https://en.wikipedia.org/wiki/Pattern_matching">potential impact</a>, is <strong>structural pattern matching</strong>. Its introduction has sometimes been referred to as <code>switch ... case</code> coming to Python, but you’ll see that structural pattern matching is much more powerful than that.
|
||
</p>
|
||
<p>
|
||
You’ll see three different examples that together highlight why this feature is called structural pattern matching and show you how you can use this new feature:
|
||
</p>
|
||
<ol>
|
||
<li>Detecting and deconstructing different <strong>structures</strong> in your data
|
||
</li>
|
||
<li>Using different kinds of <strong>patterns</strong>
|
||
</li>
|
||
<li>
|
||
<strong>Matching</strong> literal patterns
|
||
</li>
|
||
</ol>
|
||
<p>
|
||
Structural pattern matching is a comprehensive addition to the Python language. To give you a taste of how you can take advantage of it in your own projects, the next three subsections will dive into some of the details. You’ll also see some links that can help you explore in even more depth if you want.
|
||
</p>
|
||
<section class="section3">
|
||
<h3 id="deconstructing-data-structures">
|
||
Deconstructing Data Structures<a class="headerlink" href="#deconstructing-data-structures" title="Permanent link"></a>
|
||
</h3>
|
||
<p>
|
||
At its core, structural pattern matching is about defining patterns to which your data structures can be matched. In this section, you’ll study a practical example where you’ll work with data that are structured differently, even though the meaning is the same. You’ll define several patterns, and depending on which pattern matches your data, you’ll process your data appropriately.
|
||
</p>
|
||
<p>
|
||
This section will be a bit light on explanations of the possible patterns. Instead, it will try to give you an impression of the possibilities. The next section will step back and explain the patterns in more detail.
|
||
</p>
|
||
<p>
|
||
Time to match your first pattern! The following example uses a <code>match ... case</code> block to find the first name of a user by extracting it from a <code>user</code> data structure:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="pycon" data-is-repl="true">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect">
|
||
<span class="codeblock__output-toggle" title="Toggle prompts and output"><span class="icon baseline js-codeblock-output-on codeblock__header--icon-lower"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#regular--rectangle-terminal"></use></svg></span></span>
|
||
</div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="gp">>>> </span><span class="n">user</span> <span class="o">=</span> <span class="p">{</span>
|
||
<span class="gp">... </span> <span class="s2">"name"</span><span class="p">:</span> <span class="p">{</span><span class="s2">"first"</span><span class="p">:</span> <span class="s2">"Pablo"</span><span class="p">,</span> <span class="s2">"last"</span><span class="p">:</span> <span class="s2">"Galindo Salgado"</span><span class="p">},</span>
|
||
<span class="gp">... </span> <span class="s2">"title"</span><span class="p">:</span> <span class="s2">"Python 3.10 release manager"</span><span class="p">,</span>
|
||
<span class="gp">... </span><span class="p">}</span>
|
||
|
||
<span class="hll"><span class="gp">>>> </span><span class="k">match</span> <span class="n">user</span><span class="p">:</span>
|
||
</span><span class="hll"><span class="gp">... </span> <span class="k">case</span> <span class="p">{</span><span class="s2">"name"</span><span class="p">:</span> <span class="p">{</span><span class="s2">"first"</span><span class="p">:</span> <span class="n">first_name</span><span class="p">}}:</span>
|
||
</span><span class="gp">... </span> <span class="k">pass</span>
|
||
<span class="gp">...</span>
|
||
|
||
<span class="gp">>>> </span><span class="n">first_name</span>
|
||
<span class="go">'Pablo'</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
You can see structural pattern matching at work in the highlighted lines. <code>user</code> is a small dictionary with user information. The <code>case</code> line specifies a pattern that <code>user</code> is matched against. In this case, you’re looking for a dictionary with a <code>"name"</code> key whose value is a new dictionary. This nested dictionary has a key called <code>"first"</code>. The corresponding value is bound to the variable <code>first_name</code>.
|
||
</p>
|
||
<p>
|
||
For a practical example, say that you’re processing user data where the underlying data model changes over time. Therefore, you need to be able to process different versions of the same data.
|
||
</p>
|
||
<p>
|
||
In the next example, you’ll use data from <a href="https://randomuser.me">randomuser.me</a>. This is a great API for generating random user data that you can use during testing and development. The API is also an example of an API that has changed over time. You can still access the <a href="https://randomuser.me/documentation#previous">old versions</a> of the API.
|
||
</p>
|
||
<p>
|
||
You may expand the collapsed section below to see how you can use <a href="https://realpython.com/python-requests/"><code>requests</code></a> to obtain different versions of the user data using the API:
|
||
</p>
|
||
<div class="card mb-3" id="collapse_cardbb4757">
|
||
<div class="card-header border-0">
|
||
<p class="m-0">
|
||
<button class="btn w-100" data-toggle="collapse" data-target="#collapsebb4757" aria-expanded="false" aria-controls="collapsebb4757" markdown="1"><span class="float-left" markdown="1">Getting Random User Data</span><span class="float-right text-muted">Show/Hide</span></button>
|
||
</p>
|
||
</div>
|
||
<div class="collapse js-collapsible-section" data-parent="#collapse_cardbb4757" id="collapsebb4757">
|
||
<div class="card-body">
|
||
<p>
|
||
You can get a random user from the API using <code>requests</code> as follows:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="c1"># random_user.py</span>
|
||
|
||
<span class="kn">import</span> <span class="nn">requests</span>
|
||
|
||
<span class="k">def</span> <span class="nf">get_user</span><span class="p">(</span><span class="n">version</span><span class="o">=</span><span class="s2">"1.3"</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""Get random users"""</span>
|
||
<span class="n">url</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"https://randomuser.me/api/</span><span class="si">{</span><span class="n">version</span><span class="si">}</span><span class="s2">/?results=1"</span>
|
||
<span class="n">response</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">response</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="n">response</span><span class="o">.</span><span class="n">json</span><span class="p">()[</span><span class="s2">"results"</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
<code>get_user()</code> gets one random user in <a href="https://realpython.com/python-json/">JSON</a> format. Note the <code>version</code> parameter. The structure of the returned data has changed quite a bit between earlier versions like <code>"1.1"</code> and the current version <code>"1.3"</code>, but in each case, the actual user data are contained in a list inside the <code>"results"</code> array. The function returns the first—and only—user in this list.
|
||
</p>
|
||
<p>
|
||
At the time of writing, the latest version of the API is 1.3 and the data has the following structure:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="json">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--purple">
|
||
<span class="mr-2 noselect" aria-label="Language">JSON</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"gender"</span><span class="p">:</span><span class="w"> </span><span class="s2">"female"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Miss"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"first"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Ilona"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"last"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Jokela"</span>
|
||
<span class="w"> </span><span class="p">},</span>
|
||
<span class="w"> </span><span class="nt">"location"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"street"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"number"</span><span class="p">:</span><span class="w"> </span><span class="mi">4473</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Mannerheimintie"</span>
|
||
<span class="w"> </span><span class="p">},</span>
|
||
<span class="w"> </span><span class="nt">"city"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Harjavalta"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"state"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Ostrobothnia"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"country"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Finland"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"postcode"</span><span class="p">:</span><span class="w"> </span><span class="mi">44879</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"coordinates"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"latitude"</span><span class="p">:</span><span class="w"> </span><span class="s2">"-6.0321"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"longitude"</span><span class="p">:</span><span class="w"> </span><span class="s2">"123.2213"</span>
|
||
<span class="w"> </span><span class="p">},</span>
|
||
<span class="w"> </span><span class="nt">"timezone"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"offset"</span><span class="p">:</span><span class="w"> </span><span class="s2">"+5:30"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Bombay, Calcutta, Madras, New Delhi"</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="w"> </span><span class="p">},</span>
|
||
<span class="w"> </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ilona.jokela@example.com"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"login"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"632b7617-6312-4edf-9c24-d6334a6af52d"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"username"</span><span class="p">:</span><span class="w"> </span><span class="s2">"brownsnake482"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"password"</span><span class="p">:</span><span class="w"> </span><span class="s2">"biatch"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"salt"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ofk518ZW"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"md5"</span><span class="p">:</span><span class="w"> </span><span class="s2">"6d589615ca44f6e583c85d45bf431c54"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"sha1"</span><span class="p">:</span><span class="w"> </span><span class="s2">"cd87c931d579bdff77af96c09e0eea82d1edfc19"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"sha256"</span><span class="p">:</span><span class="w"> </span><span class="s2">"6038ede83d4ce74116faa67fb3b1b2e6f6898e5749b57b5a0312bd46a539214a"</span>
|
||
<span class="w"> </span><span class="p">},</span>
|
||
<span class="hll"><span class="w"> </span><span class="nt">"dob"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
</span><span class="hll"><span class="w"> </span><span class="nt">"date"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1957-05-20T08:36:09.083Z"</span><span class="p">,</span>
|
||
</span><span class="hll"><span class="w"> </span><span class="nt">"age"</span><span class="p">:</span><span class="w"> </span><span class="mi">64</span>
|
||
</span><span class="hll"><span class="w"> </span><span class="p">},</span>
|
||
</span><span class="w"> </span><span class="nt">"registered"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"date"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2006-07-30T18:39:20.050Z"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"age"</span><span class="p">:</span><span class="w"> </span><span class="mi">15</span>
|
||
<span class="w"> </span><span class="p">},</span>
|
||
<span class="w"> </span><span class="nt">"phone"</span><span class="p">:</span><span class="w"> </span><span class="s2">"07-369-318"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"cell"</span><span class="p">:</span><span class="w"> </span><span class="s2">"048-284-01-59"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"HETU"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"NaNNA204undefined"</span>
|
||
<span class="w"> </span><span class="p">},</span>
|
||
<span class="w"> </span><span class="nt">"picture"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"large"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://randomuser.me/api/portraits/women/28.jpg"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"medium"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://randomuser.me/api/portraits/med/women/28.jpg"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"thumbnail"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://randomuser.me/api/portraits/thumb/women/28.jpg"</span>
|
||
<span class="w"> </span><span class="p">},</span>
|
||
<span class="w"> </span><span class="nt">"nat"</span><span class="p">:</span><span class="w"> </span><span class="s2">"FI"</span>
|
||
<span class="p">}</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
One of the members that changed between different versions is <code>"dob"</code>, the date of birth. Note that in version 1.3, this is a JSON object with two members, <code>"date"</code> and <code>"age"</code>.
|
||
</p>
|
||
<div class="alert alert-primary" role="alert">
|
||
<p>
|
||
<strong>Note:</strong> By default, <a href="https://randomuser.me">randomuser.me</a> returns a random user. You can get the exact same user as in this example by setting the <a href="https://randomuser.me/documentation#seeds">seed</a> to <code>310</code>:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="n">url</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"https://randomuser.me/api/</span><span class="si">{</span><span class="n">version</span><span class="si">}</span><span class="s2">/?results=1&seed=310"</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
You set the seed by adding <code>&seed=310</code> to the URL. The full object returned by the API also contains some metadata in a member named <code>"info"</code>. These metadata will include the version of the data as well as the seed used to create the random user.
|
||
</p>
|
||
</div>
|
||
<p>
|
||
Compare the result above with a version 1.1 random user:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="json">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--purple">
|
||
<span class="mr-2 noselect" aria-label="Language">JSON</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"gender"</span><span class="p">:</span><span class="w"> </span><span class="s2">"female"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"miss"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"first"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ilona"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"last"</span><span class="p">:</span><span class="w"> </span><span class="s2">"jokela"</span>
|
||
<span class="w"> </span><span class="p">},</span>
|
||
<span class="w"> </span><span class="nt">"location"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"street"</span><span class="p">:</span><span class="w"> </span><span class="s2">"7336 myllypuronkatu"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"city"</span><span class="p">:</span><span class="w"> </span><span class="s2">"kurikka"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"state"</span><span class="p">:</span><span class="w"> </span><span class="s2">"central ostrobothnia"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"postcode"</span><span class="p">:</span><span class="w"> </span><span class="mi">53740</span>
|
||
<span class="w"> </span><span class="p">},</span>
|
||
<span class="w"> </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ilona.jokela@example.com"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"login"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"username"</span><span class="p">:</span><span class="w"> </span><span class="s2">"blackelephant837"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"password"</span><span class="p">:</span><span class="w"> </span><span class="s2">"sand"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"salt"</span><span class="p">:</span><span class="w"> </span><span class="s2">"yofk518Z"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"md5"</span><span class="p">:</span><span class="w"> </span><span class="s2">"b26367ea967600d679ee3e0b9bda012f"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"sha1"</span><span class="p">:</span><span class="w"> </span><span class="s2">"87d2910595acba5b8e8aa8b00a841bab08580e2f"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"sha256"</span><span class="p">:</span><span class="w"> </span><span class="s2">"73bd0d205d0dc83ae184ae222ff2e9de5ea4039119a962c4f97fabd5bbfa7aca"</span>
|
||
<span class="w"> </span><span class="p">},</span>
|
||
<span class="hll"><span class="w"> </span><span class="nt">"dob"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1966-04-17 11:57:01"</span><span class="p">,</span>
|
||
</span><span class="w"> </span><span class="nt">"registered"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2005-08-10 10:15:01"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"phone"</span><span class="p">:</span><span class="w"> </span><span class="s2">"04-636-931"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"cell"</span><span class="p">:</span><span class="w"> </span><span class="s2">"048-828-40-15"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"HETU"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"366-9204"</span>
|
||
<span class="w"> </span><span class="p">},</span>
|
||
<span class="w"> </span><span class="nt">"picture"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"large"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://randomuser.me/api/portraits/women/24.jpg"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"medium"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://randomuser.me/api/portraits/med/women/24.jpg"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"thumbnail"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://randomuser.me/api/portraits/thumb/women/24.jpg"</span>
|
||
<span class="w"> </span><span class="p">},</span>
|
||
<span class="w"> </span><span class="nt">"nat"</span><span class="p">:</span><span class="w"> </span><span class="s2">"FI"</span>
|
||
<span class="p">}</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
Observe that in this older format, the value of the <code>"dob"</code> member is a plain string.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
In this example, you’ll work with the information about the date of birth (<code>dob</code>) for each user. The structure of these data has changed between different versions of the Random User API:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="json">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--purple">
|
||
<span class="mr-2 noselect" aria-label="Language">JSON</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="err">#</span><span class="w"> </span><span class="err">Versio</span><span class="kc">n</span><span class="w"> </span><span class="mf">1.1</span>
|
||
<span class="nt">"dob"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1966-04-17 11:57:01"</span>
|
||
|
||
<span class="err">#</span><span class="w"> </span><span class="err">Versio</span><span class="kc">n</span><span class="w"> </span><span class="mf">1.3</span>
|
||
<span class="nt">"dob"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="nt">"date"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1957-05-20T08:36:09.083Z"</span><span class="p">,</span><span class="w"> </span><span class="nt">"age"</span><span class="p">:</span><span class="w"> </span><span class="mi">64</span><span class="p">}</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
Note that in version 1.1, the date of birth is represented as a simple string, while in version 1.3, it’s a JSON object with two members: <code>"date"</code> and <code>"age"</code>. Say that you want to find the age of a user. Depending on the structure of your data, you’d either need to calculate the age based on the date of birth or look up the age if it’s already available.
|
||
</p>
|
||
<div class="alert alert-primary" role="alert">
|
||
<p>
|
||
<strong>Note:</strong> The value of <code>age</code> is accurate when you download the data. If you store the data, this value will eventually become outdated. If this is a concern, you should calculate the current age based on <code>date</code>.
|
||
</p>
|
||
</div>
|
||
<p>
|
||
Traditionally, you would detect the structure of the data with an <code>if</code> test, maybe based on the type of the <code>"dob"</code> field. You can approach this differently in Python 3.10. Now, you can use structural pattern matching instead:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="linenos"> 1</span><span class="c1"># random_user.py (continued)</span>
|
||
<span class="linenos"> 2</span>
|
||
<span class="linenos"> 3</span><span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span>
|
||
<span class="linenos"> 4</span>
|
||
<span class="linenos"> 5</span><span class="k">def</span> <span class="nf">get_age</span><span class="p">(</span><span class="n">user</span><span class="p">):</span>
|
||
<span class="linenos"> 6</span><span class="w"> </span><span class="sd">"""Get the age of a user"""</span>
|
||
<span class="hll"><span class="linenos"> 7</span> <span class="k">match</span> <span class="n">user</span><span class="p">:</span>
|
||
</span><span class="hll"><span class="linenos"> 8</span> <span class="k">case</span> <span class="p">{</span><span class="s2">"dob"</span><span class="p">:</span> <span class="p">{</span><span class="s2">"age"</span><span class="p">:</span> <span class="nb">int</span><span class="p">(</span><span class="n">age</span><span class="p">)}}:</span>
|
||
</span><span class="linenos"> 9</span> <span class="k">return</span> <span class="n">age</span>
|
||
<span class="hll"><span class="linenos">10</span> <span class="k">case</span> <span class="p">{</span><span class="s2">"dob"</span><span class="p">:</span> <span class="n">dob</span><span class="p">}:</span>
|
||
</span><span class="linenos">11</span> <span class="n">now</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span>
|
||
<span class="linenos">12</span> <span class="n">dob_date</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">strptime</span><span class="p">(</span><span class="n">dob</span><span class="p">,</span> <span class="s2">"%Y-%m-</span><span class="si">%d</span><span class="s2"> %H:%M:%S"</span><span class="p">)</span>
|
||
<span class="linenos">13</span> <span class="k">return</span> <span class="n">now</span><span class="o">.</span><span class="n">year</span> <span class="o">-</span> <span class="n">dob_date</span><span class="o">.</span><span class="n">year</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
The <code>match ... case</code> construct is new in Python 3.10 and is how you perform structural pattern matching. You start with a <code>match</code> statement that specifies what you want to match. In this example, that’s the <code>user</code> data structure.
|
||
</p>
|
||
<p>
|
||
One or several <code>case</code> statements follow <code>match</code>. Each <code>case</code> describes one pattern, and the indented block beneath it says what should happen if there’s a match. In this example:
|
||
</p>
|
||
<ul>
|
||
<li>
|
||
<p>
|
||
<strong>Line 8</strong> matches a dictionary with a <code>"dob"</code> key whose value is another dictionary with an integer (<code>int</code>) item named <code>"age"</code>. The name <code>age</code> captures its value.
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<p>
|
||
<strong>Line 10</strong> matches any dictionary with a <code>"dob"</code> key. The name <code>dob</code> captures its value.
|
||
</p>
|
||
</li>
|
||
</ul>
|
||
<p>
|
||
One important feature of pattern matching is that at most one pattern will be matched. Since the pattern on line 10 matches any dictionary with <code>"dob"</code>, it’s important that the more specific pattern on line 8 comes first.
|
||
</p>
|
||
<div class="alert alert-primary" role="alert">
|
||
<p>
|
||
<strong>Note:</strong> The age calculation done on line 13 is not very precise since it ignores dates. You can improve on this by explicitly comparing months and days to check whether the user has already celebrated their birthday this year. However, a better solution would be to use <a href="https://realpython.com/python-datetime/#doing-arithmetic-with-python-datetime"><code>relativedelta</code></a> from the <a href="https://dateutil.readthedocs.io/">dateutil</a> package. By using <code>relativedelta</code>, you can calculate years directly.
|
||
</p>
|
||
</div>
|
||
<p>
|
||
Before looking closer at the details of the patterns and how they work, try calling <code>get_age()</code> with different data structures to see the result:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="pycon" data-is-repl="true">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect">
|
||
<span class="codeblock__output-toggle" title="Toggle prompts and output"><span class="icon baseline js-codeblock-output-on codeblock__header--icon-lower"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#regular--rectangle-terminal"></use></svg></span></span>
|
||
</div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="gp">>>> </span><span class="kn">import</span> <span class="nn">random_user</span>
|
||
|
||
<span class="gp">>>> </span><span class="n">users11</span> <span class="o">=</span> <span class="n">random_user</span><span class="o">.</span><span class="n">get_user</span><span class="p">(</span><span class="n">version</span><span class="o">=</span><span class="s2">"1.1"</span><span class="p">)</span>
|
||
<span class="gp">>>> </span><span class="n">random_user</span><span class="o">.</span><span class="n">get_age</span><span class="p">(</span><span class="n">users11</span><span class="p">)</span>
|
||
<span class="go">55</span>
|
||
|
||
<span class="gp">>>> </span><span class="n">users13</span> <span class="o">=</span> <span class="n">random_user</span><span class="o">.</span><span class="n">get_user</span><span class="p">(</span><span class="n">version</span><span class="o">=</span><span class="s2">"1.3"</span><span class="p">)</span>
|
||
<span class="gp">>>> </span><span class="n">random_user</span><span class="o">.</span><span class="n">get_age</span><span class="p">(</span><span class="n">users13</span><span class="p">)</span>
|
||
<span class="go">64</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
Your code can calculate the age correctly for both versions of the user data, which have different dates of birth.
|
||
</p>
|
||
<p>
|
||
Look closer at those patterns. The first pattern, <code>{"dob": {"age": int(age)}}</code>, matches version 1.3 of the user data:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="p">{</span>
|
||
<span class="o">...</span>
|
||
<span class="s2">"dob"</span><span class="p">:</span> <span class="p">{</span><span class="s2">"date"</span><span class="p">:</span> <span class="s2">"1957-05-20T08:36:09.083Z"</span><span class="p">,</span> <span class="s2">"age"</span><span class="p">:</span> <span class="mi">64</span><span class="p">},</span>
|
||
<span class="o">...</span>
|
||
<span class="p">}</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
The first pattern is a nested pattern. The outer curly braces say that a dictionary with the key <code>"dob"</code> is required. The corresponding value should be a dictionary. This nested dictionary must match the subpattern <code>{"age": int(age)}</code>. In other words, it needs to have an <code>"age"</code> key with an integer value. That value is bound to the name <code>age</code>.
|
||
</p>
|
||
<p>
|
||
The second pattern, <code>{"dob": dob}</code>, matches the older version 1.1 of the user data:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="p">{</span>
|
||
<span class="o">...</span>
|
||
<span class="s2">"dob"</span><span class="p">:</span> <span class="s2">"1966-04-17 11:57:01"</span><span class="p">,</span>
|
||
<span class="o">...</span>
|
||
<span class="p">}</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
This second pattern is a simpler pattern than the first one. Again, the curly braces indicate that it will match a dictionary. However, any dictionary with a <code>"dob"</code> key is matched because there are no other restrictions specified. The value of that key is bound to the name <code>dob</code>.
|
||
</p>
|
||
<p>
|
||
The main takeaway is that you can describe the structure of your data using mostly familiar notation. One striking change, though, is that you can use names like <code>dob</code> and <code>age</code>, which aren’t yet defined. Instead, values from your data are <strong>bound</strong> to these names when a pattern matches.
|
||
</p>
|
||
<p>
|
||
You’ve explored some of the power of structural pattern matching in this example. In the next section, you’ll dive a bit more into the details.
|
||
</p>
|
||
<div>
|
||
<div class="rounded border border-light" style="display:block;position:relative;">
|
||
<div style="display:block;width:100%;padding-top:12.5%;"></div>
|
||
<div class="rpad rounded border" data-unit="8x1" style="position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;">
|
||
<a href="https://srv.realpython.net/click/33911031100/?c=48874902176&p=58946116052&r=16822" rel="nofollow" target="_blank"><img src="https://img.realpython.net/c2032f8e413c3322e9ba1029b4adcb98" style="max-width: 100%; max-height: 100%; width: 100%;" /></a>
|
||
</div>
|
||
</div><a class="small text-muted" href="/account/join/" rel="nofollow"><span class="icon baseline mr-1"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#solid--circle-info"></use></svg></span>Remove ads</a>
|
||
</div>
|
||
</section>
|
||
<section class="section3">
|
||
<h3 id="using-different-kinds-of-patterns">
|
||
Using Different Kinds of Patterns<a class="headerlink" href="#using-different-kinds-of-patterns" title="Permanent link"></a>
|
||
</h3>
|
||
<p>
|
||
You’ve seen an example of how you can use patterns to effectively unravel complicated data structures. Now, you’ll take a step back and look at the building blocks that make up this new feature. Many things come together to make it work. In fact, there are three <a href="https://www.python.org/dev/peps/pep-0001/#what-is-a-pep">Python Enhancement Proposals</a> (PEPs) that describe structural pattern matching:
|
||
</p>
|
||
<ol>
|
||
<li>
|
||
<strong>PEP 634:</strong> <a href="https://www.python.org/dev/peps/pep-0634/">Specification</a>
|
||
</li>
|
||
<li>
|
||
<strong>PEP 635:</strong> <a href="https://www.python.org/dev/peps/pep-0635/">Motivation and Rationale</a>
|
||
</li>
|
||
<li>
|
||
<strong>PEP 636:</strong> <a href="https://www.python.org/dev/peps/pep-0636/">Tutorial</a>
|
||
</li>
|
||
</ol>
|
||
<p>
|
||
These documents give you a lot of background and detail if you’re interested in a deeper dive than what follows.
|
||
</p>
|
||
<p>
|
||
Patterns are at the center of structural pattern matching. In this section, you’ll learn about some of the different kinds of patterns that exist:
|
||
</p>
|
||
<ul>
|
||
<li>
|
||
<strong>Mapping patterns</strong> match mapping structures like dictionaries.
|
||
</li>
|
||
<li>
|
||
<strong>Sequence patterns</strong> match sequence structures like tuples and lists.
|
||
</li>
|
||
<li>
|
||
<strong>Capture patterns</strong> bind values to names.
|
||
</li>
|
||
<li>
|
||
<strong>AS patterns</strong> bind the value of subpatterns to names.
|
||
</li>
|
||
<li>
|
||
<strong>OR patterns</strong> match one of several different subpatterns.
|
||
</li>
|
||
<li>
|
||
<strong>Wildcard patterns</strong> match anything.
|
||
</li>
|
||
<li>
|
||
<strong>Class patterns</strong> match class structures.
|
||
</li>
|
||
<li>
|
||
<strong>Value patterns</strong> match values stored in attributes.
|
||
</li>
|
||
<li>
|
||
<strong>Literal patterns</strong> match literal values.
|
||
</li>
|
||
</ul>
|
||
<p>
|
||
You already used several of them in the example in the previous section. In particular, you used <strong>mapping patterns</strong> to unravel data stored in dictionaries. In this section, you’ll learn more about how some of these work. All the details are available in the PEPs mentioned above.
|
||
</p>
|
||
<p>
|
||
A <strong>capture pattern</strong> is used to capture a match to a pattern and bind it to a name. Consider the following <a href="https://realpython.com/python-recursion/">recursive</a> function that sums a list of numbers:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="linenos"> 1</span><span class="k">def</span> <span class="nf">sum_list</span><span class="p">(</span><span class="n">numbers</span><span class="p">):</span>
|
||
<span class="linenos"> 2</span> <span class="k">match</span> <span class="n">numbers</span><span class="p">:</span>
|
||
<span class="linenos"> 3</span> <span class="k">case</span> <span class="p">[]:</span>
|
||
<span class="linenos"> 4</span> <span class="k">return</span> <span class="mi">0</span>
|
||
<span class="hll"><span class="linenos"> 5</span> <span class="k">case</span> <span class="p">[</span><span class="n">first</span><span class="p">,</span> <span class="o">*</span><span class="n">rest</span><span class="p">]:</span>
|
||
</span><span class="linenos"> 6</span> <span class="k">return</span> <span class="n">first</span> <span class="o">+</span> <span class="n">sum_list</span><span class="p">(</span><span class="n">rest</span><span class="p">)</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
The first <code>case</code> on line 3 matches the empty list and returns <code>0</code> as its sum. The second <code>case</code> on line 5 uses a <strong>sequence pattern</strong> with two capture patterns to match lists with one or more elements. The first element in the list is captured and bound to the name <code>first</code>. The second capture pattern, <code>*rest</code>, uses <a href="https://realpython.com/python-kwargs-and-args/#unpacking-with-the-asterisk-operators">unpacking syntax</a> to match any number of elements. <code>rest</code> will bind to a list containing all elements of <code>numbers</code> except the first one.
|
||
</p>
|
||
<p>
|
||
<code>sum_list()</code> calculates the sum of a list of numbers by recursively adding the first number in the list and the sum of the rest of the numbers. You can use it as follows:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="pycon" data-is-repl="true">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect">
|
||
<span class="codeblock__output-toggle" title="Toggle prompts and output"><span class="icon baseline js-codeblock-output-on codeblock__header--icon-lower"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#regular--rectangle-terminal"></use></svg></span></span>
|
||
</div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="gp">>>> </span><span class="n">sum_list</span><span class="p">([</span><span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">9</span><span class="p">,</span> <span class="mi">4</span><span class="p">])</span>
|
||
<span class="go">22</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
The sum of 4 + 5 + 9 + 4 is correctly calculated to be 22. As an exercise for yourself, you can try to trace the recursive calls to <code>sum_list()</code> to make sure you understand how the code sums the whole list.
|
||
</p>
|
||
<div class="alert alert-primary" role="alert">
|
||
<p>
|
||
<strong>Note:</strong> Capture patterns essentially assign values to variables. However, one limitation is that only undotted names are allowed. In other words, you can’t use a capture pattern to assign to a <a href="https://realpython.com/python3-object-oriented-programming/#class-and-instance-attributes">class or instance attribute</a> directly.
|
||
</p>
|
||
</div>
|
||
<p>
|
||
<code>sum_list()</code> handles summing up a list of numbers. Observe what happens if you try to sum anything that isn’t a list:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="pycon" data-is-repl="true">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect">
|
||
<span class="codeblock__output-toggle" title="Toggle prompts and output"><span class="icon baseline js-codeblock-output-on codeblock__header--icon-lower"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#regular--rectangle-terminal"></use></svg></span></span>
|
||
</div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="gp">>>> </span><span class="nb">print</span><span class="p">(</span><span class="n">sum_list</span><span class="p">(</span><span class="s2">"4594"</span><span class="p">))</span>
|
||
<span class="go">None</span>
|
||
|
||
<span class="gp">>>> </span><span class="nb">print</span><span class="p">(</span><span class="n">sum_list</span><span class="p">(</span><span class="mi">4594</span><span class="p">))</span>
|
||
<span class="go">None</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
Passing a string or a number to <code>sum_list()</code> returns <code>None</code>. This occurs because none of the patterns match, and the execution continues after the <code>match</code> block. That happens to be the end of the function, so <code>sum_list()</code> implicitly <a href="https://realpython.com/python-return-statement/#implicit-return-statements">returns <code>None</code></a>.
|
||
</p>
|
||
<p>
|
||
Often, though, you want to be alerted about failed matches. You can add a catchall pattern as the final case that handles this by raising an error, for example. You can use the underscore (<code>_</code>) as a <strong>wildcard pattern</strong> that matches anything without binding it to a name. You can add some error handling to <code>sum_list()</code> as follows:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="k">def</span> <span class="nf">sum_list</span><span class="p">(</span><span class="n">numbers</span><span class="p">):</span>
|
||
<span class="k">match</span> <span class="n">numbers</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="p">[]:</span>
|
||
<span class="k">return</span> <span class="mi">0</span>
|
||
<span class="k">case</span> <span class="p">[</span><span class="n">first</span><span class="p">,</span> <span class="o">*</span><span class="n">rest</span><span class="p">]:</span>
|
||
<span class="k">return</span> <span class="n">first</span> <span class="o">+</span> <span class="n">sum_list</span><span class="p">(</span><span class="n">rest</span><span class="p">)</span>
|
||
<span class="hll"> <span class="k">case</span><span class="w"> </span><span class="k">_</span><span class="p">:</span>
|
||
</span> <span class="n">wrong_type</span> <span class="o">=</span> <span class="n">numbers</span><span class="o">.</span><span class="vm">__class__</span><span class="o">.</span><span class="vm">__name__</span>
|
||
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Can only sum lists, not </span><span class="si">{</span><span class="n">wrong_type</span><span class="si">!r}</span><span class="s2">"</span><span class="p">)</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
The final <code>case</code> will match anything that doesn’t match the first two patterns. This will raise a descriptive error, for instance, if you try to calculate <code>sum_list(4594)</code>. This is useful when you need to alert your users that some input was not matched as expected.
|
||
</p>
|
||
<p>
|
||
Your patterns are still not foolproof, though. Consider what happens if you try to sum a list of strings:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="pycon" data-is-repl="true">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect">
|
||
<span class="codeblock__output-toggle" title="Toggle prompts and output"><span class="icon baseline js-codeblock-output-on codeblock__header--icon-lower"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#regular--rectangle-terminal"></use></svg></span></span>
|
||
</div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="gp">>>> </span><span class="n">sum_list</span><span class="p">([</span><span class="s2">"45"</span><span class="p">,</span> <span class="s2">"94"</span><span class="p">])</span>
|
||
<span class="go">TypeError: can only concatenate str (not "int") to str</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
The base case returns <code>0</code>, so therefore the summing only works for types that you can add with numbers. Python doesn’t know how to add numbers and text strings together. You can restrict your pattern to only match integers using a <strong>class pattern</strong>:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="k">def</span> <span class="nf">sum_list</span><span class="p">(</span><span class="n">numbers</span><span class="p">):</span>
|
||
<span class="k">match</span> <span class="n">numbers</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="p">[]:</span>
|
||
<span class="k">return</span> <span class="mi">0</span>
|
||
<span class="hll"> <span class="k">case</span> <span class="p">[</span><span class="nb">int</span><span class="p">(</span><span class="n">first</span><span class="p">),</span> <span class="o">*</span><span class="n">rest</span><span class="p">]:</span>
|
||
</span> <span class="k">return</span> <span class="n">first</span> <span class="o">+</span> <span class="n">sum_list</span><span class="p">(</span><span class="n">rest</span><span class="p">)</span>
|
||
<span class="k">case</span><span class="w"> </span><span class="k">_</span><span class="p">:</span>
|
||
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Can only sum lists of numbers"</span><span class="p">)</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
Adding <code>int()</code> around <code>first</code> makes sure that the pattern only matches if the value is an integer. This might be too restrictive, though. Your function should be able to sum both <a href="https://realpython.com/python-numbers/#integers">integers</a> and <a href="https://realpython.com/python-numbers/#floating-point-numbers">floating-point numbers</a>, so how can you allow this in your pattern?
|
||
</p>
|
||
<p>
|
||
To check whether at least one out of several subpatterns match, you can use an <strong>OR pattern</strong>. OR patterns consist of two or more subpatterns, and the pattern matches if at least one of the subpatterns does. You can use this to match when the first element is either of type <code>int</code> or type <code>float</code>:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="k">def</span> <span class="nf">sum_list</span><span class="p">(</span><span class="n">numbers</span><span class="p">):</span>
|
||
<span class="k">match</span> <span class="n">numbers</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="p">[]:</span>
|
||
<span class="k">return</span> <span class="mi">0</span>
|
||
<span class="hll"> <span class="k">case</span> <span class="p">[</span><span class="nb">int</span><span class="p">(</span><span class="n">first</span><span class="p">)</span> <span class="o">|</span> <span class="nb">float</span><span class="p">(</span><span class="n">first</span><span class="p">),</span> <span class="o">*</span><span class="n">rest</span><span class="p">]:</span>
|
||
</span> <span class="k">return</span> <span class="n">first</span> <span class="o">+</span> <span class="n">sum_list</span><span class="p">(</span><span class="n">rest</span><span class="p">)</span>
|
||
<span class="k">case</span><span class="w"> </span><span class="k">_</span><span class="p">:</span>
|
||
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Can only sum lists of numbers"</span><span class="p">)</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
You use the pipe symbol (<code>|</code>) to separate the subpatterns in an OR pattern. Your function now allows summing a list of floating-point numbers:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="pycon" data-is-repl="true">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect">
|
||
<span class="codeblock__output-toggle" title="Toggle prompts and output"><span class="icon baseline js-codeblock-output-on codeblock__header--icon-lower"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#regular--rectangle-terminal"></use></svg></span></span>
|
||
</div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="gp">>>> </span><span class="n">sum_list</span><span class="p">([</span><span class="mf">45.94</span><span class="p">,</span> <span class="mf">46.17</span><span class="p">,</span> <span class="mf">46.72</span><span class="p">])</span>
|
||
<span class="go">138.82999999999998</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
There’s a lot of power and flexibility within structural pattern matching, even more than what you’ve seen so far. Some things that aren’t covered in this overview are:
|
||
</p>
|
||
<ul>
|
||
<li>Using <a href="https://www.python.org/dev/peps/pep-0635/#guards">guards</a> to restrict patterns
|
||
</li>
|
||
<li>Using <a href="https://www.python.org/dev/peps/pep-0635/#as-patterns">AS patterns</a> to capture the value of subpatterns
|
||
</li>
|
||
<li>Using <a href="https://www.python.org/dev/peps/pep-0636/#matching-positional-attributes">class patterns</a> to match custom <a href="https://docs.python.org/3/library/enum.html">enums</a> and <a href="https://realpython.com/python-data-classes/">data classes</a>
|
||
</li>
|
||
</ul>
|
||
<p>
|
||
If you’re interested, have a look in the documentation to learn more about these features as well. In the next section, you’ll learn about literal patterns and value patterns.
|
||
</p>
|
||
<div>
|
||
<div class="rounded border border-light" style="display:block;position:relative;">
|
||
<div style="display:block;width:100%;padding-top:12.5%;"></div>
|
||
<div class="rpad rounded border" data-unit="8x1" style="position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;">
|
||
<a href="https://srv.realpython.net/click/8941932849/?c=48874902176&p=58946116052&r=33909" rel="nofollow" target="_blank"><img src="https://img.realpython.net/8dd76836b26ce79fa0afa59df2458580" style="max-width: 100%; max-height: 100%; width: 100%;" /></a>
|
||
</div>
|
||
</div><a class="small text-muted" href="/account/join/" rel="nofollow"><span class="icon baseline mr-1"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#solid--circle-info"></use></svg></span>Remove ads</a>
|
||
</div>
|
||
</section>
|
||
<section class="section3">
|
||
<h3 id="matching-literal-patterns">
|
||
Matching Literal Patterns<a class="headerlink" href="#matching-literal-patterns" title="Permanent link"></a>
|
||
</h3>
|
||
<p>
|
||
A <strong>literal pattern</strong> is a pattern that matches a literal object like an explicit string or number. In a sense, this is the most basic kind of pattern and allows you to emulate <code>switch ... case</code> statements seen in other languages. The following example matches a specific name:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="k">def</span> <span class="nf">greet</span><span class="p">(</span><span class="n">name</span><span class="p">):</span>
|
||
<span class="k">match</span> <span class="n">name</span><span class="p">:</span>
|
||
<span class="hll"> <span class="k">case</span> <span class="s2">"Guido"</span><span class="p">:</span>
|
||
</span> <span class="nb">print</span><span class="p">(</span><span class="s2">"Hi, Guido!"</span><span class="p">)</span>
|
||
<span class="k">case</span><span class="w"> </span><span class="k">_</span><span class="p">:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"Howdy, stranger!"</span><span class="p">)</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
The first <code>case</code> matches the literal string <code>"Guido"</code>. In this case, you use <code>_</code> as a wildcard to print a generic greeting whenever <code>name</code> is not <code>"Guido"</code>. Such literal patterns can sometimes take the place of <code>if ... elif ... else</code> constructs and can play the same role that <code>switch ... case</code> does in some other languages.
|
||
</p>
|
||
<p>
|
||
One limitation with structural pattern matching is that you can’t directly match values stored in variables. Say that you’ve defined <code>bdfl = "Guido"</code>. A pattern like <code>case bdfl:</code> will not match <code>"Guido"</code>. Instead, this will be interpreted as a capture pattern that matches anything and binds that value to <code>bdfl</code>, effectively overwriting the old value.
|
||
</p>
|
||
<p>
|
||
You can, however, use a <strong>value pattern</strong> to match stored values. A value pattern looks a bit like a capture pattern but uses a previously defined dotted name that holds the value that will be matched against.
|
||
</p>
|
||
<div class="alert alert-primary" role="alert">
|
||
<p>
|
||
<strong>Note:</strong> A <strong>dotted name</strong> is a name with a dot (<code>.</code>) in it. In practice, this will reference an attribute of either a class, an instance of a class, an enumeration, or a module.
|
||
</p>
|
||
</div>
|
||
<p>
|
||
You can, for example, use an <a href="https://docs.python.org/3/library/enum.html">enumeration</a> to create such dotted names:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="kn">import</span> <span class="nn">enum</span>
|
||
|
||
<span class="k">class</span> <span class="nc">Pythonista</span><span class="p">(</span><span class="nb">str</span><span class="p">,</span> <span class="n">enum</span><span class="o">.</span><span class="n">Enum</span><span class="p">):</span>
|
||
<span class="n">BDFL</span> <span class="o">=</span> <span class="s2">"Guido"</span>
|
||
<span class="n">FLUFL</span> <span class="o">=</span> <span class="s2">"Barry"</span>
|
||
|
||
<span class="k">def</span> <span class="nf">greet</span><span class="p">(</span><span class="n">name</span><span class="p">):</span>
|
||
<span class="k">match</span> <span class="n">name</span><span class="p">:</span>
|
||
<span class="hll"> <span class="k">case</span> <span class="n">Pythonista</span><span class="o">.</span><span class="n">BDFL</span><span class="p">:</span>
|
||
</span> <span class="nb">print</span><span class="p">(</span><span class="s2">"Hi, Guido!"</span><span class="p">)</span>
|
||
<span class="k">case</span><span class="w"> </span><span class="k">_</span><span class="p">:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"Howdy, stranger!"</span><span class="p">)</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
The first case now uses a value pattern to match <code>Pythonista.BDFL</code>, which is <code>"Guido"</code>. Note that you can use any dotted name in a value pattern. You could, for example, have used a regular class or a module instead of the enumeration.
|
||
</p>
|
||
<p>
|
||
To see a bigger example of how to use literal patterns, consider the game of <a href="https://en.wikipedia.org/wiki/Fizz_buzz">FizzBuzz</a>. This is a counting game where you should replace some numbers with words according to the following rules:
|
||
</p>
|
||
<ul>
|
||
<li>You replace numbers divisible by <strong>3</strong> with <strong>fizz</strong>.
|
||
</li>
|
||
<li>You replace numbers divisible by <strong>5</strong> with <strong>buzz</strong>.
|
||
</li>
|
||
<li>You replace numbers divisible by both <strong>3</strong> and <strong>5</strong> with <strong>fizzbuzz</strong>.
|
||
</li>
|
||
</ul>
|
||
<p>
|
||
FizzBuzz is sometimes used to introduce conditionals in programming education and as a screening problem in interviews. Even though a solution is quite straightforward, <a href="https://twitter.com/joelgrus">Joel Grus</a> has written a full <a href="https://fizzbuzzbook.com/">book</a> about different ways to program the game.
|
||
</p>
|
||
<p>
|
||
A typical solution in Python will use <code>if ... elif ... else</code> as follows:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="k">def</span> <span class="nf">fizzbuzz</span><span class="p">(</span><span class="n">number</span><span class="p">):</span>
|
||
<span class="n">mod_3</span> <span class="o">=</span> <span class="n">number</span> <span class="o">%</span> <span class="mi">3</span>
|
||
<span class="n">mod_5</span> <span class="o">=</span> <span class="n">number</span> <span class="o">%</span> <span class="mi">5</span>
|
||
|
||
<span class="k">if</span> <span class="n">mod_3</span> <span class="o">==</span> <span class="mi">0</span> <span class="ow">and</span> <span class="n">mod_5</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="s2">"fizzbuzz"</span>
|
||
<span class="k">elif</span> <span class="n">mod_3</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="s2">"fizz"</span>
|
||
<span class="k">elif</span> <span class="n">mod_5</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="s2">"buzz"</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="nb">str</span><span class="p">(</span><span class="n">number</span><span class="p">)</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
The <a href="https://realpython.com/python-modulo-operator/"><code>%</code> operator</a> calculates the modulus, which you can use to <a href="https://realpython.com/python-modulo-operator/#python-modulo-operator-in-practice">test divisibility</a>. Namely, if <em>a</em> modulus <em>b</em> is 0 for two numbers <em>a</em> and <em>b</em>, then <em>a</em> is divisible by <em>b</em>.
|
||
</p>
|
||
<p>
|
||
In <code>fizzbuzz()</code>, you calculate <code>number % 3</code> and <code>number % 5</code>, which you then use to test for divisibility with 3 and 5. Note that you must do the test for divisibility with both 3 and 5 first. If not, numbers that are divisible by both 3 and 5 will be covered by either the <code>"fizz"</code> or the <code>"buzz"</code> cases instead.
|
||
</p>
|
||
<p>
|
||
You can check that your implementation gives the expected result:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="pycon" data-is-repl="true">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect">
|
||
<span class="codeblock__output-toggle" title="Toggle prompts and output"><span class="icon baseline js-codeblock-output-on codeblock__header--icon-lower"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#regular--rectangle-terminal"></use></svg></span></span>
|
||
</div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="gp">>>> </span><span class="n">fizzbuzz</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
|
||
<span class="go">fizz</span>
|
||
|
||
<span class="gp">>>> </span><span class="n">fizzbuzz</span><span class="p">(</span><span class="mi">14</span><span class="p">)</span>
|
||
<span class="go">14</span>
|
||
|
||
<span class="gp">>>> </span><span class="n">fizzbuzz</span><span class="p">(</span><span class="mi">15</span><span class="p">)</span>
|
||
<span class="go">fizzbuzz</span>
|
||
|
||
<span class="gp">>>> </span><span class="n">fizzbuzz</span><span class="p">(</span><span class="mi">92</span><span class="p">)</span>
|
||
<span class="go">92</span>
|
||
|
||
<span class="gp">>>> </span><span class="n">fizzbuzz</span><span class="p">(</span><span class="mi">65</span><span class="p">)</span>
|
||
<span class="go">buzz</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
You can confirm for yourself that 3 is divisible by 3, 65 is divisible by 5, and 15 is divisible by both 3 and 5, while 14 and 92 aren’t divisible by either 3 or 5.
|
||
</p>
|
||
<p>
|
||
An <code>if ... elif ... else</code> structure where you’re comparing one or a few variables several times over is quite straightforward to rewrite using pattern matching instead. For example, you can do the following:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="k">def</span> <span class="nf">fizzbuzz</span><span class="p">(</span><span class="n">number</span><span class="p">):</span>
|
||
<span class="n">mod_3</span> <span class="o">=</span> <span class="n">number</span> <span class="o">%</span> <span class="mi">3</span>
|
||
<span class="n">mod_5</span> <span class="o">=</span> <span class="n">number</span> <span class="o">%</span> <span class="mi">5</span>
|
||
|
||
<span class="k">match</span> <span class="p">(</span><span class="n">mod_3</span><span class="p">,</span> <span class="n">mod_5</span><span class="p">):</span>
|
||
<span class="k">case</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="s2">"fizzbuzz"</span>
|
||
<span class="k">case</span><span class="w"> </span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="k">_</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="s2">"fizz"</span>
|
||
<span class="k">case</span><span class="w"> </span><span class="p">(</span><span class="k">_</span><span class="p">,</span> <span class="mi">0</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="s2">"buzz"</span>
|
||
<span class="k">case</span><span class="w"> </span><span class="k">_</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="nb">str</span><span class="p">(</span><span class="n">number</span><span class="p">)</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
You match on both <code>mod_3</code> and <code>mod_5</code>. Each <code>case</code> pattern then matches either the literal number <code>0</code> or the wildcard <code>_</code> on the corresponding values.
|
||
</p>
|
||
<p>
|
||
Compare and contrast this version with the previous one. Note how the pattern <code>(0, 0)</code> corresponds to the test <code>mod_3 == 0 and mod_5 == 0</code>, while <code>(0, _)</code> corresponds to <code>mod_3 == 0</code>.
|
||
</p>
|
||
<p>
|
||
As you saw earlier, you can use an OR pattern to match on several different patterns. For example, since <code>mod_3</code> can only take the values <code>0</code>, <code>1</code>, and <code>2</code>, you can replace <code>case (_, 0)</code> with <code>case (1, 0) | (2, 0)</code>. Remember that <code>(0, 0)</code> has already been covered.
|
||
</p>
|
||
<div class="alert alert-primary" role="alert">
|
||
<p>
|
||
<strong>Note:</strong> If you’ve been using <code>switch ... case</code> in other languages, you should remember that there’s no <a href="https://en.wikipedia.org/wiki/Switch_statement#Fallthrough">fallthrough</a> in Python’s pattern matching. That means that at most one <code>case</code> will ever be executed, namely the first <code>case</code> that matches. This is different from languages like C and Java. You can handle most of the effects of fallthroughs with OR patterns.
|
||
</p>
|
||
</div>
|
||
<p>
|
||
The Python core developers have <a href="https://www.python.org/dev/peps/pep-3103/">consciously chosen</a> not to include <code>switch ... case</code> statements in the language earlier. However, there are some third-party packages that do, like <a href="https://pypi.org/project/switchlang/">switchlang</a>, which adds a <code>switch</code> command that also works on earlier versions of Python.
|
||
</p>
|
||
<div>
|
||
<div class="rounded border border-light" style="display:block;position:relative;">
|
||
<div style="display:block;width:100%;padding-top:12.5%;"></div>
|
||
<div class="rpad rounded border" data-unit="8x1" style="position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;">
|
||
<a href="https://srv.realpython.net/click/11225539816/?c=48874902176&p=58946116052&r=53422" rel="nofollow" target="_blank"><img src="https://img.realpython.net/cae48de6b6f52aa978e17ea36747a9fc" style="max-width: 100%; max-height: 100%; width: 100%;" /></a>
|
||
</div>
|
||
</div><a class="small text-muted" href="/account/join/" rel="nofollow"><span class="icon baseline mr-1"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#solid--circle-info"></use></svg></span>Remove ads</a>
|
||
</div>
|
||
</section>
|
||
</section>
|
||
<section class="section2">
|
||
<h2 id="type-unions-aliases-and-guards">
|
||
Type Unions, Aliases, and Guards<a class="headerlink" href="#type-unions-aliases-and-guards" title="Permanent link"></a>
|
||
</h2>
|
||
<p>
|
||
Reliably, each new Python release brings some improvements to the <a href="https://realpython.com/python-type-checking/">static typing</a> system. Python 3.10 is no exception. In fact, four different PEPs about typing accompany this new release:
|
||
</p>
|
||
<ol>
|
||
<li>
|
||
<strong>PEP 604:</strong> <a href="https://www.python.org/dev/peps/pep-0604">Allow writing union types as <code>X | Y</code></a>
|
||
</li>
|
||
<li>
|
||
<strong>PEP 613:</strong> <a href="https://www.python.org/dev/peps/pep-0613">Explicit Type Aliases</a>
|
||
</li>
|
||
<li>
|
||
<strong>PEP 647:</strong> <a href="https://www.python.org/dev/peps/pep-0647/">User-Defined Type Guards</a>
|
||
</li>
|
||
<li>
|
||
<strong>PEP 612:</strong> <a href="https://www.python.org/dev/peps/pep-0612/">Parameter Specification Variables</a>
|
||
</li>
|
||
</ol>
|
||
<p>
|
||
PEP 604 will probably be the most widely used of these changes going forward, but you’ll get a brief overview of each of the features in this section.
|
||
</p>
|
||
<p>
|
||
You can use <strong>union types</strong> to declare that a variable can have one of several different types. For example, you’ve been able to type hint a function calculating the mean of a list of numbers, floats, or integers as follows:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">List</span><span class="p">,</span> <span class="n">Union</span>
|
||
|
||
<span class="hll"><span class="k">def</span> <span class="nf">mean</span><span class="p">(</span><span class="n">numbers</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">Union</span><span class="p">[</span><span class="nb">float</span><span class="p">,</span> <span class="nb">int</span><span class="p">]])</span> <span class="o">-></span> <span class="nb">float</span><span class="p">:</span>
|
||
</span> <span class="k">return</span> <span class="nb">sum</span><span class="p">(</span><span class="n">numbers</span><span class="p">)</span> <span class="o">/</span> <span class="nb">len</span><span class="p">(</span><span class="n">numbers</span><span class="p">)</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
The annotation <code>List[Union[float, int]]</code> means that <code>numbers</code> should be a list where each element is either a floating-point number or an integer. This works well, but the notation is a bit verbose. Also, you need to import both <code>List</code> and <code>Union</code> from <code>typing</code>.
|
||
</p>
|
||
<div class="alert alert-primary" role="alert">
|
||
<p>
|
||
<strong>Note:</strong> The implementation of <code>mean()</code> looks straightforward, but there are actually several <a href="https://www.python.org/dev/peps/pep-0450/#rationale">corner cases</a> where it will fail. If you need to calculate means, you should use <a href="https://docs.python.org/3/library/statistics.html#statistics.mean"><code>statistics.mean()</code></a> instead.
|
||
</p>
|
||
</div>
|
||
<p>
|
||
In Python 3.10, you can replace <code>Union[float, int]</code> with the more succinct <code>float | int</code>. Combine this with the ability to use <a href="https://realpython.com/python-list/"><code>list</code></a> instead of <code>typing.List</code> in type hints, which <a href="https://realpython.com/python39-new-features/#type-hint-lists-and-dictionaries-directly">Python 3.9</a> introduced. You can then simplify your code while keeping all the type information:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="hll"><span class="k">def</span> <span class="nf">mean</span><span class="p">(</span><span class="n">numbers</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">float</span> <span class="o">|</span> <span class="nb">int</span><span class="p">])</span> <span class="o">-></span> <span class="nb">float</span><span class="p">:</span>
|
||
</span> <span class="k">return</span> <span class="nb">sum</span><span class="p">(</span><span class="n">numbers</span><span class="p">)</span> <span class="o">/</span> <span class="nb">len</span><span class="p">(</span><span class="n">numbers</span><span class="p">)</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
The annotation of <code>numbers</code> is easier to read now, and as an added bonus, you didn’t need to import anything from <code>typing</code>.
|
||
</p>
|
||
<p>
|
||
A special case of union types is when a variable can have either a specific type or be <code>None</code>. You can annotate such <strong>optional types</strong> either as <code>Union[None, T]</code> or, equivalently, <a href="https://realpython.com/python-type-checking/#the-optional-type"><code>Optional[T]</code></a> for some type <code>T</code>. There is no new, special syntax for optional types, but you can use the new union syntax to avoid importing <code>typing.Optional</code>:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="n">address</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
In this example, <code>address</code> is allowed to be either <code>None</code> or a string.
|
||
</p>
|
||
<p>
|
||
You can also use the new union syntax at runtime in <code>isinstance()</code> or <code>issubclass()</code> tests:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="pycon" data-is-repl="true">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect">
|
||
<span class="codeblock__output-toggle" title="Toggle prompts and output"><span class="icon baseline js-codeblock-output-on codeblock__header--icon-lower"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#regular--rectangle-terminal"></use></svg></span></span>
|
||
</div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="gp">>>> </span><span class="nb">isinstance</span><span class="p">(</span><span class="s2">"mypy"</span><span class="p">,</span> <span class="nb">str</span> <span class="o">|</span> <span class="nb">int</span><span class="p">)</span>
|
||
<span class="go">True</span>
|
||
|
||
<span class="gp">>>> </span><span class="nb">issubclass</span><span class="p">(</span><span class="nb">str</span><span class="p">,</span> <span class="nb">int</span> <span class="o">|</span> <span class="nb">float</span> <span class="o">|</span> <span class="nb">bytes</span><span class="p">)</span>
|
||
<span class="go">False</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
Traditionally, you’ve used tuples to test for several types at once—for example, <code>(str, int)</code> instead of <code>str | int</code>. This old syntax will still work.
|
||
</p>
|
||
<p>
|
||
<strong>Type aliases</strong> allow you to quickly <a href="https://realpython.com/python-type-checking/#type-aliases">define new aliases</a> that can stand in for more complicated type declarations. For example, say that you’re <a href="https://realpython.com/python-type-checking/#example-a-deck-of-cards">representing a playing card</a> using a tuple of suit and rank strings and a deck of cards by a list of such playing card tuples. A deck of cards is then type hinted as <code>list[tuple[str, str]]</code>.
|
||
</p>
|
||
<p>
|
||
To simplify type annotation, you define type aliases as follows:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="n">Card</span> <span class="o">=</span> <span class="nb">tuple</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">str</span><span class="p">]</span>
|
||
<span class="n">Deck</span> <span class="o">=</span> <span class="nb">list</span><span class="p">[</span><span class="n">Card</span><span class="p">]</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
This usually works okay. However, it’s often not possible for the type checker to know whether such a statement is a type alias or just the definition of a regular global variable. To help the type checker—or really, help the type checker help you—you can now explicitly annotate type aliases:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">TypeAlias</span>
|
||
|
||
<span class="n">Card</span><span class="p">:</span> <span class="n">TypeAlias</span> <span class="o">=</span> <span class="nb">tuple</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">str</span><span class="p">]</span>
|
||
<span class="n">Deck</span><span class="p">:</span> <span class="n">TypeAlias</span> <span class="o">=</span> <span class="nb">list</span><span class="p">[</span><span class="n">Card</span><span class="p">]</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
Adding the <code>TypeAlias</code> annotation clarifies the intention, both to a type checker and to anyone reading your code.
|
||
</p>
|
||
<p>
|
||
<strong>Type guards</strong> are used to narrow down union types. The following function takes in either a string or <code>None</code> but always returns a tuple of strings representing a playing card:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="k">def</span> <span class="nf">get_ace</span><span class="p">(</span><span class="n">suit</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span><span class="p">)</span> <span class="o">-></span> <span class="nb">tuple</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">str</span><span class="p">]:</span>
|
||
<span class="hll"> <span class="k">if</span> <span class="n">suit</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||
</span> <span class="n">suit</span> <span class="o">=</span> <span class="s2">"♠"</span>
|
||
<span class="k">return</span> <span class="p">(</span><span class="n">suit</span><span class="p">,</span> <span class="s2">"A"</span><span class="p">)</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
The highlighted line works as a type guard, and static type checkers are able to realize that <code>suit</code> is necessarily a string when it’s returned.
|
||
</p>
|
||
<p>
|
||
Currently, the type checkers can only use a <a href="https://www.python.org/dev/peps/pep-0647/#motivation">few different constructs</a> to narrow down union types in this way. With the new <a href="https://docs.python.org/3.10/library/typing.html#typing.TypeGuard"><code>typing.TypeGuard</code></a>, you can annotate custom functions that can be used to narrow down union types:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Any</span><span class="p">,</span> <span class="n">TypeAlias</span><span class="p">,</span> <span class="n">TypeGuard</span>
|
||
|
||
<span class="n">Card</span><span class="p">:</span> <span class="n">TypeAlias</span> <span class="o">=</span> <span class="nb">tuple</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">str</span><span class="p">]</span>
|
||
<span class="n">Deck</span><span class="p">:</span> <span class="n">TypeAlias</span> <span class="o">=</span> <span class="nb">list</span><span class="p">[</span><span class="n">Card</span><span class="p">]</span>
|
||
|
||
<span class="hll"><span class="k">def</span> <span class="nf">is_deck_of_cards</span><span class="p">(</span><span class="n">obj</span><span class="p">:</span> <span class="n">Any</span><span class="p">)</span> <span class="o">-></span> <span class="n">TypeGuard</span><span class="p">[</span><span class="n">Deck</span><span class="p">]:</span>
|
||
</span> <span class="c1"># Return True if obj is a deck of cards, otherwise False</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
<code>is_deck_of_cards()</code> should return <code>True</code> or <code>False</code> depending on whether <code>obj</code> represents a <code>Deck</code> object or not. You can then use your guard function, and the type checker will be able to narrow down the types correctly:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="k">def</span> <span class="nf">get_score</span><span class="p">(</span><span class="n">card_or_deck</span><span class="p">:</span> <span class="n">Card</span> <span class="o">|</span> <span class="n">Deck</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
|
||
<span class="hll"> <span class="k">if</span> <span class="n">is_deck_of_cards</span><span class="p">(</span><span class="n">card_or_deck</span><span class="p">):</span>
|
||
</span> <span class="c1"># Calculate score of a deck of cards</span>
|
||
<span class="o">...</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
Inside of the <code>if</code> block, the type checker knows that <code>card_or_deck</code> is, in fact, of the type <code>Deck</code>. See <a href="https://www.python.org/dev/peps/pep-0647/">PEP 647</a> for more details.
|
||
</p>
|
||
<p>
|
||
The final new typing feature is <strong>Parameter Specification Variables</strong>, which is related to <a href="https://realpython.com/python-type-checking/#type-variables">type variables</a>. Consider the definition of a <a href="https://realpython.com/primer-on-python-decorators/">decorator</a>. In general, it looks something like the following:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="kn">import</span> <span class="nn">functools</span>
|
||
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Any</span><span class="p">,</span> <span class="n">Callable</span><span class="p">,</span> <span class="n">TypeVar</span>
|
||
|
||
<span class="n">R</span> <span class="o">=</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s2">"R"</span><span class="p">)</span>
|
||
|
||
<span class="hll"><span class="k">def</span> <span class="nf">decorator</span><span class="p">(</span><span class="n">func</span><span class="p">:</span> <span class="n">Callable</span><span class="p">[</span><span class="o">...</span><span class="p">,</span> <span class="n">R</span><span class="p">])</span> <span class="o">-></span> <span class="n">Callable</span><span class="p">[</span><span class="o">...</span><span class="p">,</span> <span class="n">R</span><span class="p">]:</span>
|
||
</span> <span class="nd">@functools</span><span class="o">.</span><span class="n">wraps</span><span class="p">(</span><span class="n">func</span><span class="p">)</span>
|
||
<span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">:</span> <span class="n">Any</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="n">Any</span><span class="p">)</span> <span class="o">-></span> <span class="n">R</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
<span class="k">return</span> <span class="n">wrapper</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
The annotations mean that the function returned by the decorator is a callable with some parameters and the same return type, <code>R</code>, as the function passed into the decorator. The <a href="https://realpython.com/python-ellipsis/">ellipsis</a> (<code>...</code>) in the function header correctly allows any number of parameters, and each of those parameters can be of any type. However, there’s no validation that the returned callable has the same parameters as the function that was passed in. In practice, this means that type checkers aren’t able to check decorated functions properly.
|
||
</p>
|
||
<p>
|
||
Unfortunately, you can’t use <code>TypeVar</code> for the parameters because you don’t know how many parameters the function will have. In Python 3.10, you’ll have access to <a href="https://docs.python.org/3.10/library/typing.html#typing.ParamSpec"><code>ParamSpec</code></a> in order to type hint these kinds of callables properly. <code>ParamSpec</code> works similarly to <code>TypeVar</code> but stands in for several parameters at once. You can rewrite your decorator as follows to take advantage of <code>ParamSpec</code>:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="kn">import</span> <span class="nn">functools</span>
|
||
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Callable</span><span class="p">,</span> <span class="n">ParamSpec</span><span class="p">,</span> <span class="n">TypeVar</span>
|
||
|
||
<span class="n">P</span> <span class="o">=</span> <span class="n">ParamSpec</span><span class="p">(</span><span class="s2">"P"</span><span class="p">)</span>
|
||
<span class="n">R</span> <span class="o">=</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s2">"R"</span><span class="p">)</span>
|
||
|
||
<span class="k">def</span> <span class="nf">decorator</span><span class="p">(</span><span class="n">func</span><span class="p">:</span> <span class="n">Callable</span><span class="p">[</span><span class="n">P</span><span class="p">,</span> <span class="n">R</span><span class="p">])</span> <span class="o">-></span> <span class="n">Callable</span><span class="p">[</span><span class="n">P</span><span class="p">,</span> <span class="n">R</span><span class="p">]:</span>
|
||
<span class="nd">@functools</span><span class="o">.</span><span class="n">wraps</span><span class="p">(</span><span class="n">func</span><span class="p">)</span>
|
||
<span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">:</span> <span class="n">P</span><span class="o">.</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="n">P</span><span class="o">.</span><span class="n">kwargs</span><span class="p">)</span> <span class="o">-></span> <span class="n">R</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
<span class="k">return</span> <span class="n">wrapper</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
Note that you also use <code>P</code> when you annotate <code>wrapper()</code>. You can also use the new <a href="https://docs.python.org/3.10/library/typing.html#typing.Concatenate"><code>typing.Concatenate</code></a> to add types to <code>ParamSpec</code>. See the <a href="https://docs.python.org/3.10/library/typing.html">documentation</a> and <a href="https://www.python.org/dev/peps/pep-0612/">PEP 612</a> for details and examples.
|
||
</p>
|
||
<div>
|
||
<div class="rounded border border-light" style="display:block;position:relative;">
|
||
<div style="display:block;width:100%;padding-top:12.5%;"></div>
|
||
<div class="rpad rounded border" data-unit="8x1" style="position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;">
|
||
<a href="https://srv.realpython.net/click/27944979224/?c=48874902176&p=58946116052&r=96550" rel="nofollow" target="_blank"><img src="https://img.realpython.net/eb1fdbdd9dab50bc687468a6fffd3d0b" style="max-width: 100%; max-height: 100%; width: 100%;" /></a>
|
||
</div>
|
||
</div><a class="small text-muted" href="/account/join/" rel="nofollow"><span class="icon baseline mr-1"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#solid--circle-info"></use></svg></span>Remove ads</a>
|
||
</div>
|
||
</section>
|
||
<section class="section2">
|
||
<h2 id="stricter-zipping-of-sequences">
|
||
Stricter Zipping of Sequences<a class="headerlink" href="#stricter-zipping-of-sequences" title="Permanent link"></a>
|
||
</h2>
|
||
<p>
|
||
<a href="https://realpython.com/python-zip-function/"><code>zip()</code></a> is a <a href="https://docs.python.org/3/library/functions.html#built-in-functions">built-in function</a> in Python that can combine elements from several sequences. Python 3.10 introduces the new <code>strict</code> parameter, which adds a runtime test to check that all sequences being zipped have the same length.
|
||
</p>
|
||
<p>
|
||
As an example, consider the following table of <a href="https://www.lego.com/">Lego</a> sets:
|
||
</p>
|
||
<div class="table-responsive">
|
||
<table class="table table-hover">
|
||
<thead>
|
||
<tr>
|
||
<th>
|
||
Name
|
||
</th>
|
||
<th>
|
||
Set Number
|
||
</th>
|
||
<th>
|
||
Pieces
|
||
</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>
|
||
<a href="https://brickset.com/sets/21024-1/Louvre">Louvre</a>
|
||
</td>
|
||
<td>
|
||
21024
|
||
</td>
|
||
<td>
|
||
695
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td>
|
||
<a href="https://brickset.com/sets/75978-1/Diagon-Alley">Diagon Alley</a>
|
||
</td>
|
||
<td>
|
||
75978
|
||
</td>
|
||
<td>
|
||
5544
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td>
|
||
<a href="https://brickset.com/sets/92176-1/NASA-Apollo-Saturn-V">NASA Apollo Saturn V</a>
|
||
</td>
|
||
<td>
|
||
92176
|
||
</td>
|
||
<td>
|
||
1969
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td>
|
||
<a href="https://brickset.com/sets/75192-1/Millennium-Falcon">Millennium Falcon</a>
|
||
</td>
|
||
<td>
|
||
75192
|
||
</td>
|
||
<td>
|
||
7541
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td>
|
||
<a href="https://brickset.com/sets/21028-1/New-York-City">New York City</a>
|
||
</td>
|
||
<td>
|
||
21028
|
||
</td>
|
||
<td>
|
||
598
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<p>
|
||
One way to represent these data in plain Python would be with each column as a list. It could look something like this:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="pycon" data-is-repl="true">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect">
|
||
<span class="codeblock__output-toggle" title="Toggle prompts and output"><span class="icon baseline js-codeblock-output-on codeblock__header--icon-lower"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#regular--rectangle-terminal"></use></svg></span></span>
|
||
</div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="gp">>>> </span><span class="n">names</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"Louvre"</span><span class="p">,</span> <span class="s2">"Diagon Alley"</span><span class="p">,</span> <span class="s2">"Saturn V"</span><span class="p">,</span> <span class="s2">"Millennium Falcon"</span><span class="p">,</span> <span class="s2">"NYC"</span><span class="p">]</span>
|
||
<span class="gp">>>> </span><span class="n">set_numbers</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"21024"</span><span class="p">,</span> <span class="s2">"75978"</span><span class="p">,</span> <span class="s2">"92176"</span><span class="p">,</span> <span class="s2">"75192"</span><span class="p">,</span> <span class="s2">"21028"</span><span class="p">]</span>
|
||
<span class="gp">>>> </span><span class="n">num_pieces</span> <span class="o">=</span> <span class="p">[</span><span class="mi">695</span><span class="p">,</span> <span class="mi">5544</span><span class="p">,</span> <span class="mi">1969</span><span class="p">,</span> <span class="mi">7541</span><span class="p">,</span> <span class="mi">598</span><span class="p">]</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
Note that you have three independent lists, but there’s an implicit correspondence between their elements. The first name (<code>"Louvre"</code>), the first set number (<code>"21024"</code>), and the first number of pieces (<code>695</code>) all describe the first Lego set.
|
||
</p>
|
||
<div class="alert alert-primary" role="alert">
|
||
<p>
|
||
<strong>Note:</strong> <a href="https://realpython.com/pandas-dataframe/">pandas</a> is great for working with and manipulating these kinds of tabular data. However, if you’re doing smaller calculations, you may not want to introduce such a heavy dependency into your project.
|
||
</p>
|
||
</div>
|
||
<p>
|
||
<code>zip()</code> can be used to iterate over these three lists in parallel:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="pycon" data-is-repl="true">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect">
|
||
<span class="codeblock__output-toggle" title="Toggle prompts and output"><span class="icon baseline js-codeblock-output-on codeblock__header--icon-lower"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#regular--rectangle-terminal"></use></svg></span></span>
|
||
</div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="gp">>>> </span><span class="k">for</span> <span class="n">name</span><span class="p">,</span> <span class="n">num</span><span class="p">,</span> <span class="n">pieces</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="n">names</span><span class="p">,</span> <span class="n">set_numbers</span><span class="p">,</span> <span class="n">num_pieces</span><span class="p">):</span>
|
||
<span class="gp">... </span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="n">num</span><span class="si">}</span><span class="s2">): </span><span class="si">{</span><span class="n">pieces</span><span class="si">}</span><span class="s2"> pieces"</span><span class="p">)</span>
|
||
<span class="gp">...</span>
|
||
<span class="go">Louvre (21024): 695 pieces</span>
|
||
<span class="go">Diagon Alley (75978): 5544 pieces</span>
|
||
<span class="go">Saturn V (92176): 1969 pieces</span>
|
||
<span class="go">Millennium Falcon (75192): 7541 pieces</span>
|
||
<span class="go">NYC (21028): 598 pieces</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
Note how each line collects information from all three lists and shows information about one particular set. This is a very common pattern that’s used in a lot of different Python code, including <a href="https://www.python.org/dev/peps/pep-0618/#examples">in the standard library</a>.
|
||
</p>
|
||
<p>
|
||
You can also add <code>list()</code> to collect the contents of all three lists in a single, nested list of tuples:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="pycon" data-is-repl="true">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect">
|
||
<span class="codeblock__output-toggle" title="Toggle prompts and output"><span class="icon baseline js-codeblock-output-on codeblock__header--icon-lower"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#regular--rectangle-terminal"></use></svg></span></span>
|
||
</div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="gp">>>> </span><span class="nb">list</span><span class="p">(</span><span class="nb">zip</span><span class="p">(</span><span class="n">names</span><span class="p">,</span> <span class="n">set_numbers</span><span class="p">,</span> <span class="n">num_pieces</span><span class="p">))</span>
|
||
<span class="go">[('Louvre', '21024', 695),</span>
|
||
<span class="go"> ('Diagon Alley', '75978', 5544),</span>
|
||
<span class="go"> ('Saturn V', '92176', 1969),</span>
|
||
<span class="go"> ('Millennium Falcon', '75192', 7541),</span>
|
||
<span class="go"> ('NYC', '21028', 598)]</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
Note how the nested list closely resembles the original table.
|
||
</p>
|
||
<p>
|
||
The dark side of using <code>zip()</code> is that it’s quite easy to introduce a subtle bug that can be hard to discover. Note what happens if there’s a missing item in one of your lists:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="pycon" data-is-repl="true">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect">
|
||
<span class="codeblock__output-toggle" title="Toggle prompts and output"><span class="icon baseline js-codeblock-output-on codeblock__header--icon-lower"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#regular--rectangle-terminal"></use></svg></span></span>
|
||
</div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="gp">>>> </span><span class="n">set_numbers</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"21024"</span><span class="p">,</span> <span class="s2">"75978"</span><span class="p">,</span> <span class="s2">"75192"</span><span class="p">,</span> <span class="s2">"21028"</span><span class="p">]</span> <span class="c1"># Saturn V missing</span>
|
||
|
||
<span class="gp">>>> </span><span class="nb">list</span><span class="p">(</span><span class="nb">zip</span><span class="p">(</span><span class="n">names</span><span class="p">,</span> <span class="n">set_numbers</span><span class="p">,</span> <span class="n">num_pieces</span><span class="p">))</span>
|
||
<span class="go">[('Louvre', '21024', 695),</span>
|
||
<span class="go"> ('Diagon Alley', '75978', 5544),</span>
|
||
<span class="go"> ('Saturn V', '75192', 1969),</span>
|
||
<span class="go"> ('Millennium Falcon', '21028', 7541)]</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
All the information about the New York City set disappeared! Additionally, the set numbers for Saturn V and Millennium Falcon are wrong. If your datasets are bigger, these kinds of errors can be very hard to discover. And even when you observe that something’s wrong, it’s not always easy to diagnose and fix.
|
||
</p>
|
||
<p>
|
||
The issue is that you assumed that the three lists have the same number of elements and that the information is in the same order in each list. After <code>set_numbers</code> gets corrupted, this assumption is no longer true.
|
||
</p>
|
||
<p>
|
||
<a href="https://www.python.org/dev/peps/pep-0618/">PEP 618</a> introduces a new <code>strict</code> keyword parameter to <code>zip()</code> that you can use to confirm all sequences have the same length. In your example, it would raise an error alerting you to the corrupted list:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="pycon" data-is-repl="true">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect">
|
||
<span class="codeblock__output-toggle" title="Toggle prompts and output"><span class="icon baseline js-codeblock-output-on codeblock__header--icon-lower"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#regular--rectangle-terminal"></use></svg></span></span>
|
||
</div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="hll"><span class="gp">>>> </span><span class="nb">list</span><span class="p">(</span><span class="nb">zip</span><span class="p">(</span><span class="n">names</span><span class="p">,</span> <span class="n">set_numbers</span><span class="p">,</span> <span class="n">num_pieces</span><span class="p">,</span> <span class="n">strict</span><span class="o">=</span><span class="kc">True</span><span class="p">))</span>
|
||
</span><span class="gt">Traceback (most recent call last):</span>
|
||
File <span class="nb">"<stdin>"</span>, line <span class="m">1</span>, in <span class="n"><module></span>
|
||
<span class="gr">ValueError</span>: <span class="n">zip() argument 2 is shorter than argument 1</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
When the iteration reaches the New York City Lego set, the second argument <code>set_numbers</code> is already exhausted, while there are still elements left in the first argument <code>names</code>. Instead of silently giving the wrong result, your code fails with an error, and you can take action to find and fix the mistake.
|
||
</p>
|
||
<p>
|
||
There are use cases when you want to combine sequences of unequal length. Expand the box below to see how <code>zip()</code> and <code>itertools.zip_longest()</code> handle these:
|
||
</p>
|
||
<div class="card mb-3" id="collapse_cardc07d76">
|
||
<div class="card-header border-0">
|
||
<p class="m-0">
|
||
<button class="btn w-100" data-toggle="collapse" data-target="#collapsec07d76" aria-expanded="false" aria-controls="collapsec07d76" markdown="1"><span class="float-left" markdown="1">Sidebar: Zipping Sequences of Unequal Length</span><span class="float-right text-muted">Show/Hide</span></button>
|
||
</p>
|
||
</div>
|
||
<div class="collapse js-collapsible-section" data-parent="#collapse_cardc07d76" id="collapsec07d76">
|
||
<div class="card-body">
|
||
<p>
|
||
The <a href="https://realpython.com/python-itertools/#what-is-itertools-and-why-should-you-use-it">following idiom</a> divides the Lego sets into pairs:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="pycon" data-is-repl="true">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect">
|
||
<span class="codeblock__output-toggle" title="Toggle prompts and output"><span class="icon baseline js-codeblock-output-on codeblock__header--icon-lower"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#regular--rectangle-terminal"></use></svg></span></span>
|
||
</div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="gp">>>> </span><span class="n">num_per_group</span> <span class="o">=</span> <span class="mi">2</span>
|
||
<span class="gp">>>> </span><span class="nb">list</span><span class="p">(</span><span class="nb">zip</span><span class="p">(</span><span class="o">*</span><span class="p">[</span><span class="nb">iter</span><span class="p">(</span><span class="n">names</span><span class="p">)]</span> <span class="o">*</span> <span class="n">num_per_group</span><span class="p">))</span>
|
||
<span class="go">[('Louvre', 'Diagon Alley'), ('Saturn V', 'Millennium Falcon')]</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
There are five sets, a number that doesn’t divide evenly into pairs. In this case, the default behavior of <code>zip()</code>, where the last element is dropped, might make sense. You could use <code>strict=True</code> here as well, but that would raise an error when your list can’t be split into pairs. A third option, which could be the best in this case, is to use <a href="https://docs.python.org/3/library/itertools.html#itertools.zip_longest"><code>zip_longest()</code></a> from the <a href="https://realpython.com/python-itertools/"><code>itertools</code></a> standard library.
|
||
</p>
|
||
<p>
|
||
As the name suggests, <code>zip_longest()</code> combines sequences until the longest sequence is exhausted. If you use <code>zip_longest()</code> to divide the Lego sets, it becomes more explicit that New York City doesn’t have any pairing:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="pycon" data-is-repl="true">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect">
|
||
<span class="codeblock__output-toggle" title="Toggle prompts and output"><span class="icon baseline js-codeblock-output-on codeblock__header--icon-lower"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#regular--rectangle-terminal"></use></svg></span></span>
|
||
</div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="gp">>>> </span><span class="kn">from</span> <span class="nn">itertools</span> <span class="kn">import</span> <span class="n">zip_longest</span>
|
||
|
||
<span class="gp">>>> </span><span class="nb">list</span><span class="p">(</span><span class="n">zip_longest</span><span class="p">(</span><span class="o">*</span><span class="p">[</span><span class="nb">iter</span><span class="p">(</span><span class="n">names</span><span class="p">)]</span> <span class="o">*</span> <span class="n">num_per_group</span><span class="p">,</span> <span class="n">fillvalue</span><span class="o">=</span><span class="s2">""</span><span class="p">))</span>
|
||
<span class="go">[('Louvre', 'Diagon Alley'),</span>
|
||
<span class="go"> ('Saturn V', 'Millennium Falcon'),</span>
|
||
<span class="go"> ('NYC', '')]</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
Note that <code>'NYC'</code> shows up in the last tuple together with an empty string. You can control what’s filled in for missing values with the <code>fillvalue</code> parameter.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
While <code>strict</code> is not really adding any new functionality to <code>zip()</code>, it can help you avoid those hard-to-find bugs.
|
||
</p>
|
||
<div>
|
||
<div class="rounded border border-light" style="display:block;position:relative;">
|
||
<div style="display:block;width:100%;padding-top:12.5%;"></div>
|
||
<div class="rpad rounded border" data-unit="8x1" style="position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;">
|
||
<a href="https://srv.realpython.net/click/58257184031/?c=31941813441&p=58946116052&r=92516" rel="nofollow" target="_blank"><img src="https://img.realpython.net/16bf1efe41b538fae54711c58c701f0e" style="max-width: 100%; max-height: 100%; width: 100%;" /></a>
|
||
</div>
|
||
</div><a class="small text-muted" href="/account/join/" rel="nofollow"><span class="icon baseline mr-1"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#solid--circle-info"></use></svg></span>Remove ads</a>
|
||
</div>
|
||
</section>
|
||
<section class="section2">
|
||
<h2 id="new-functions-in-the-statistics-module">
|
||
New Functions in the <code>statistics</code> Module<a class="headerlink" href="#new-functions-in-the-statistics-module" title="Permanent link"></a>
|
||
</h2>
|
||
<p>
|
||
The <a href="https://docs.python.org/3/library/statistics.html"><code>statistics</code></a> module was added to the standard library all the way back in 2014 with the release of <a href="https://www.python.org/downloads/release/python-340/">Python 3.4</a>. The intent of <code>statistics</code> is to make <a href="https://realpython.com/python-statistics/">statistical calculations</a> at the <a href="https://www.python.org/dev/peps/pep-0450/">level of graphing calculators</a> available in Python.
|
||
</p>
|
||
<div class="alert alert-primary" role="alert">
|
||
<p>
|
||
<strong>Note:</strong> <code>statistics</code> isn’t designed to offer dedicated numerical data types or full-featured statistical modeling. If the standard library doesn’t cover your needs, have a look at third-party packages like <a href="https://realpython.com/numpy-tutorial/">NumPy</a>, <a href="https://docs.scipy.org/doc/scipy/reference/stats.html">SciPy</a>, <a href="https://realpython.com/pandas-python-explore-dataset/">pandas</a>, <a href="https://www.statsmodels.org/">statsmodels</a>, <a href="http://docs.pymc.io/">PyMC3</a>, <a href="https://scikit-learn.org/">scikit-learn</a>, or <a href="https://seaborn.pydata.org/">seaborn</a>.
|
||
</p>
|
||
</div>
|
||
<p>
|
||
Python 3.10 adds a few multivariable functions to <code>statistics</code>:
|
||
</p>
|
||
<ul>
|
||
<li>
|
||
<strong><code>correlation()</code></strong> to calculate Pearson’s <a href="https://realpython.com/numpy-scipy-pandas-correlation-python/">correlation</a> coefficient for two variables
|
||
</li>
|
||
<li>
|
||
<strong><code>covariance()</code></strong> to calculate sample <a href="https://en.wikipedia.org/wiki/Covariance">covariance</a> for two variables
|
||
</li>
|
||
<li>
|
||
<strong><code>linear_regression()</code></strong> to calculate the slope and intercept in a <a href="https://realpython.com/linear-regression-in-python/">linear regression</a>
|
||
</li>
|
||
</ul>
|
||
<p>
|
||
You can use each function to describe a certain aspect of the relationship between two variables. As an example, say that you have data from a set of blog posts—the number of words in each blog post and the number of views each post has had over some time period:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="pycon" data-is-repl="true">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect">
|
||
<span class="codeblock__output-toggle" title="Toggle prompts and output"><span class="icon baseline js-codeblock-output-on codeblock__header--icon-lower"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#regular--rectangle-terminal"></use></svg></span></span>
|
||
</div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="gp">>>> </span><span class="n">words</span> <span class="o">=</span> <span class="p">[</span><span class="mi">7742</span><span class="p">,</span> <span class="mi">11539</span><span class="p">,</span> <span class="mi">16898</span><span class="p">,</span> <span class="mi">13447</span><span class="p">,</span> <span class="mi">4608</span><span class="p">,</span> <span class="mi">6628</span><span class="p">,</span> <span class="mi">2683</span><span class="p">,</span> <span class="mi">6156</span><span class="p">,</span> <span class="mi">2623</span><span class="p">,</span> <span class="mi">6948</span><span class="p">]</span>
|
||
<span class="gp">>>> </span><span class="n">views</span> <span class="o">=</span> <span class="p">[</span><span class="mi">8368</span><span class="p">,</span> <span class="mi">5901</span><span class="p">,</span> <span class="mi">3978</span><span class="p">,</span> <span class="mi">3329</span><span class="p">,</span> <span class="mi">2611</span><span class="p">,</span> <span class="mi">2096</span><span class="p">,</span> <span class="mi">1515</span><span class="p">,</span> <span class="mi">1177</span><span class="p">,</span> <span class="mi">814</span><span class="p">,</span> <span class="mi">467</span><span class="p">]</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
You now want to investigate whether there’s any (linear) relationship between the number of words and number of views. In Python 3.10, you can calculate the <strong>correlation</strong> between <code>words</code> and <code>views</code> with the new <a href="https://docs.python.org/3.10/library/statistics.html#statistics.correlation"><code>correlation()</code></a> function:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="pycon" data-is-repl="true">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect">
|
||
<span class="codeblock__output-toggle" title="Toggle prompts and output"><span class="icon baseline js-codeblock-output-on codeblock__header--icon-lower"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#regular--rectangle-terminal"></use></svg></span></span>
|
||
</div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="gp">>>> </span><span class="kn">import</span> <span class="nn">statistics</span>
|
||
|
||
<span class="gp">>>> </span><span class="n">statistics</span><span class="o">.</span><span class="n">correlation</span><span class="p">(</span><span class="n">words</span><span class="p">,</span> <span class="n">views</span><span class="p">)</span>
|
||
<span class="go">0.454180067865917</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
The correlation between two variables is always a number between -1 and 1. If it’s close to 0, then there’s little correspondence between them, while a correlation close to -1 or 1 indicates that the behaviors of the two variables tend to follow each other. In this example, a correlation of 0.45 indicates that there’s a tendency for posts with more words to have more views, although it’s not a strong connection.
|
||
</p>
|
||
<div class="alert alert-primary" role="alert">
|
||
<p>
|
||
<strong>Note:</strong> The common adage <a href="https://en.wikipedia.org/wiki/Correlation_does_not_imply_causation">correlation does not imply causation</a> is important to keep in mind. Even if you find that two variables are strongly correlated, you <a href="https://tylervigen.com/spurious-correlations">can’t conclude</a> that one is the cause of the other.
|
||
</p>
|
||
</div>
|
||
<p>
|
||
You can also calculate the <strong>covariance</strong> between <code>words</code> and <code>views</code>. The covariance is another measure of the joint variability between two variables. You can calculate it with <a href="https://docs.python.org/3.10/library/statistics.html#statistics.covariance"><code>covariance()</code></a>:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="pycon" data-is-repl="true">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect">
|
||
<span class="codeblock__output-toggle" title="Toggle prompts and output"><span class="icon baseline js-codeblock-output-on codeblock__header--icon-lower"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#regular--rectangle-terminal"></use></svg></span></span>
|
||
</div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="gp">>>> </span><span class="kn">import</span> <span class="nn">statistics</span>
|
||
|
||
<span class="gp">>>> </span><span class="n">statistics</span><span class="o">.</span><span class="n">covariance</span><span class="p">(</span><span class="n">words</span><span class="p">,</span> <span class="n">views</span><span class="p">)</span>
|
||
<span class="go">5292289.977777777</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
In contrast to correlation, covariance is an absolute measure. It should be interpreted in the context of the variability within the variables themselves. In fact, you can normalize the covariance by the <a href="https://en.wikipedia.org/wiki/Standard_deviation">standard deviation</a> of each variable to recover <a href="https://en.wikipedia.org/wiki/Pearson_correlation_coefficient">Pearson’s correlation coefficient</a>:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="pycon" data-is-repl="true">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect">
|
||
<span class="codeblock__output-toggle" title="Toggle prompts and output"><span class="icon baseline js-codeblock-output-on codeblock__header--icon-lower"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#regular--rectangle-terminal"></use></svg></span></span>
|
||
</div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="gp">>>> </span><span class="kn">import</span> <span class="nn">statistics</span>
|
||
|
||
<span class="gp">>>> </span><span class="n">cov</span> <span class="o">=</span> <span class="n">statistics</span><span class="o">.</span><span class="n">covariance</span><span class="p">(</span><span class="n">words</span><span class="p">,</span> <span class="n">views</span><span class="p">)</span>
|
||
<span class="gp">>>> </span><span class="n">σ_words</span><span class="p">,</span> <span class="n">σ_views</span> <span class="o">=</span> <span class="n">statistics</span><span class="o">.</span><span class="n">stdev</span><span class="p">(</span><span class="n">words</span><span class="p">),</span> <span class="n">statistics</span><span class="o">.</span><span class="n">stdev</span><span class="p">(</span><span class="n">views</span><span class="p">)</span>
|
||
<span class="gp">>>> </span><span class="n">cov</span> <span class="o">/</span> <span class="p">(</span><span class="n">σ_words</span> <span class="o">*</span> <span class="n">σ_views</span><span class="p">)</span>
|
||
<span class="go">0.454180067865917</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
Note that this matches your earlier correlation coefficient exactly.
|
||
</p>
|
||
<p>
|
||
A third way of looking at the linear correspondence between the two variables is through <strong>simple linear regression</strong>. You do the <a href="https://en.wikipedia.org/wiki/Simple_linear_regression">linear regression</a> by calculating two numbers, <em>slope</em> and <em>intercept</em>, so that the (squared) error is minimized in the approximation <em>number of views</em> = <em>slope</em> × <em>number of words</em> + <em>intercept</em>.
|
||
</p>
|
||
<p>
|
||
In Python 3.10, you can use <a href="https://docs.python.org/3.10/library/statistics.html#statistics.linear_regression"><code>linear_regression()</code></a>:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="pycon" data-is-repl="true">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect">
|
||
<span class="codeblock__output-toggle" title="Toggle prompts and output"><span class="icon baseline js-codeblock-output-on codeblock__header--icon-lower"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#regular--rectangle-terminal"></use></svg></span></span>
|
||
</div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="gp">>>> </span><span class="kn">import</span> <span class="nn">statistics</span>
|
||
|
||
<span class="gp">>>> </span><span class="n">statistics</span><span class="o">.</span><span class="n">linear_regression</span><span class="p">(</span><span class="n">words</span><span class="p">,</span> <span class="n">views</span><span class="p">)</span>
|
||
<span class="go">LinearRegression(slope=0.2424443064354672, intercept=1103.6954940247645)</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
Based on this regression, a post with 10,074 words could expect about 0.2424 × 10074 + 1104 = 3546 views. However, as you saw earlier, the correlation between the number of words and the number of views is quite weak. Therefore, you shouldn’t expect this prediction to be very accurate.
|
||
</p>
|
||
<p>
|
||
The <code>LinearRegression</code> object is a <a href="https://realpython.com/python-namedtuple/">named tuple</a>. This means that you can unpack the slope and intercept directly:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="pycon" data-is-repl="true">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect">
|
||
<span class="codeblock__output-toggle" title="Toggle prompts and output"><span class="icon baseline js-codeblock-output-on codeblock__header--icon-lower"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#regular--rectangle-terminal"></use></svg></span></span>
|
||
</div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="gp">>>> </span><span class="kn">import</span> <span class="nn">statistics</span>
|
||
|
||
<span class="gp">>>> </span><span class="n">slope</span><span class="p">,</span> <span class="n">intercept</span> <span class="o">=</span> <span class="n">statistics</span><span class="o">.</span><span class="n">linear_regression</span><span class="p">(</span><span class="n">words</span><span class="p">,</span> <span class="n">views</span><span class="p">)</span>
|
||
<span class="gp">>>> </span><span class="n">slope</span> <span class="o">*</span> <span class="mi">10074</span> <span class="o">+</span> <span class="n">intercept</span>
|
||
<span class="go">3546.0794370556605</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
Here, you use <code>slope</code> and <code>intercept</code> to predict the number of views on a blog post with 10,074 words.
|
||
</p>
|
||
<p>
|
||
You still want to use some of the more advanced packages like pandas and statsmodels if you do a lot of statistical analysis. With the new additions to <code>statistics</code> in Python 3.10, however, you have the chance to do basic analysis more easily without bringing in third-party dependencies.
|
||
</p>
|
||
<div>
|
||
<div class="rounded border border-light" style="display:block;position:relative;">
|
||
<div style="display:block;width:100%;padding-top:12.5%;"></div>
|
||
<div class="rpad rounded border" data-unit="8x1" style="position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;">
|
||
<a href="https://srv.realpython.net/click/44043106944/?c=30337084727&p=58946116052&r=39407" rel="nofollow" target="_blank"><img src="https://img.realpython.net/39d33f31dff9ce5e91d62b0a7b0c7420" style="max-width: 100%; max-height: 100%; width: 100%;" /></a>
|
||
</div>
|
||
</div><a class="small text-muted" href="/account/join/" rel="nofollow"><span class="icon baseline mr-1"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#solid--circle-info"></use></svg></span>Remove ads</a>
|
||
</div>
|
||
</section>
|
||
<section class="section2">
|
||
<h2 id="other-pretty-cool-features">
|
||
Other Pretty Cool Features<a class="headerlink" href="#other-pretty-cool-features" title="Permanent link"></a>
|
||
</h2>
|
||
<p>
|
||
So far, you’ve seen the biggest and most impactful new features in Python 3.10. In this section, you’ll get a glimpse of a few of the other changes that the new version brings along. If you’re curious about all the changes made for this new version, check out the <a href="https://docs.python.org/3.10/whatsnew/3.10.html">documentation</a>.
|
||
</p>
|
||
<section class="section3">
|
||
<h3 id="default-text-encodings">
|
||
Default Text Encodings<a class="headerlink" href="#default-text-encodings" title="Permanent link"></a>
|
||
</h3>
|
||
<p>
|
||
When you open a text file, the default encoding used to interpret the characters is system dependent. In particular, <a href="https://docs.python.org/3.10/library/locale.html#locale.getpreferredencoding"><code>locale.getpreferredencoding()</code></a> is used. On Mac and Linux, this usually returns <code>"UTF-8"</code>, while the result on Windows is more varied.
|
||
</p>
|
||
<p>
|
||
You should therefore always specify an encoding when you attempt to open a text file:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s2">"some_file.txt"</span><span class="p">,</span> <span class="n">mode</span><span class="o">=</span><span class="s2">"r"</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="s2">"utf-8"</span><span class="p">)</span> <span class="k">as</span> <span class="n">file</span><span class="p">:</span>
|
||
<span class="o">...</span> <span class="c1"># Do something with file</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
If you don’t explicitly specify an encoding, the preferred locale encoding is used, and you could experience that a file that can be read on one computer fails to open on another.
|
||
</p>
|
||
<p>
|
||
Python 3.7 introduced <a href="https://docs.python.org/3.10/library/os.html#utf8-mode">UTF-8 mode</a>, which allows you to force your programs to use UTF-8 encoding independent of the locale encoding. You can enable UTF-8 mode by giving the <code>-X utf8</code> command-line option to the <code>python</code> executable or by setting the <code>PYTHONUTF8</code> environment variable.
|
||
</p>
|
||
<p>
|
||
In Python 3.10, you can activate a warning that will tell you when a text file is opened without a specified encoding. Consider the following script, which doesn’t specify an encoding:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="c1"># mirror.py</span>
|
||
|
||
<span class="kn">import</span> <span class="nn">pathlib</span>
|
||
<span class="kn">import</span> <span class="nn">sys</span>
|
||
|
||
<span class="k">def</span> <span class="nf">mirror_file</span><span class="p">(</span><span class="n">filename</span><span class="p">):</span>
|
||
<span class="hll"> <span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="n">mode</span><span class="o">=</span><span class="s2">"r"</span><span class="p">):</span>
|
||
</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">line</span><span class="o">.</span><span class="n">rstrip</span><span class="p">()[::</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span><span class="si">:</span><span class="s2">>72</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||
|
||
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">"__main__"</span><span class="p">:</span>
|
||
<span class="k">for</span> <span class="n">filename</span> <span class="ow">in</span> <span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">:]:</span>
|
||
<span class="n">mirror_file</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
The program will echo one or more text files back to the console, but with each line reversed. Run the program on itself with the <a href="https://docs.python.org/3.10/library/io.html#io-encoding-warning">encoding warning</a> enabled:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="console" data-is-repl="true">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--yellow">
|
||
<span class="mr-2 noselect" aria-label="Language">Shell</span>
|
||
<div class="noselect">
|
||
<span class="codeblock__output-toggle" title="Toggle prompts and output"><span class="icon baseline js-codeblock-output-on codeblock__header--icon-lower"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#regular--rectangle-terminal"></use></svg></span></span>
|
||
</div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="gp">$ </span>python<span class="w"> </span>-X<span class="w"> </span>warn_default_encoding<span class="w"> </span>mirror.py<span class="w"> </span>mirror.py
|
||
<span class="hll"><span class="go">/home/rp/mirror.py:7: EncodingWarning: 'encoding' argument not specified</span>
|
||
</span><span class="hll"><span class="go"> for line in pathlib.Path(filename).open(mode="r"):</span>
|
||
</span><span class="go"> yp.rorrim #</span>
|
||
|
||
<span class="go"> bilhtap tropmi</span>
|
||
<span class="go"> sys tropmi</span>
|
||
|
||
<span class="go"> :)emanelif(elif_rorrim fed</span>
|
||
<span class="go"> :)"r"=edom(nepo.)emanelif(htaP.bilhtap ni enil rof</span>
|
||
<span class="go"> )"}27>:]1-::[)(pirtsr.enil{"f(tnirp</span>
|
||
|
||
<span class="go"> :"__niam__" == __eman__ fi</span>
|
||
<span class="go"> :]:1[vgra.sys ni emanelif rof</span>
|
||
<span class="go"> )emanelif(elif_rorrim</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
Note the <code>EncodingWarning</code> printed to the console. The command-line option <code>-X warn_default_encoding</code> activates it. The warning will disappear if you specify an encoding—for example, <code>encoding="utf-8"</code>—when you open the file.
|
||
</p>
|
||
<p>
|
||
There are times when you want to use the user-defined local encoding. You can still do so by explicitly using <code>encoding="locale"</code>. However, it’s recommended to use UTF-8 whenever possible. You can check out <a href="https://www.python.org/dev/peps/pep-0597/">PEP 597</a> for more information.
|
||
</p>
|
||
</section>
|
||
<section class="section3">
|
||
<h3 id="asynchronous-iteration">
|
||
Asynchronous Iteration<a class="headerlink" href="#asynchronous-iteration" title="Permanent link"></a>
|
||
</h3>
|
||
<p>
|
||
<a href="https://realpython.com/python-async-features/">Asynchronous programming</a> is a powerful programming paradigm that’s been available in Python <a href="https://www.python.org/dev/peps/pep-0492/">since version 3.5</a>. You can recognize an asynchronous program by its use of the <code>async</code> keyword or <a href="https://realpython.com/python-classes/#special-methods-and-protocols">special methods</a> that <a href="https://www.python.org/dev/peps/pep-0492/#why-magic-methods-start-with-a">start with <code>.__a</code></a> like <a href="https://docs.python.org/3/reference/datamodel.html#object.__aiter__"><code>.__aiter__()</code></a> or <a href="https://docs.python.org/3/reference/datamodel.html#object.__aenter__"><code>.__aenter__()</code></a>.
|
||
</p>
|
||
<p>
|
||
In Python 3.10, two new asynchronous <a href="https://docs.python.org/3/library/functions.html#built-in-functions">built-in functions</a> are added: <a href="https://docs.python.org/3.10/library/functions.html#aiter"><code>aiter()</code></a> and <a href="https://docs.python.org/3.10/library/functions.html#anext"><code>anext()</code></a>. In practice, these functions call the <code>.__aiter__()</code> and <code>.__anext__()</code> special methods—analogous to the regular <code>iter()</code> and <code>next()</code>—so no new functionality is added. These are convenience functions that make your code more readable.
|
||
</p>
|
||
<p>
|
||
In other words, in the newest version of Python, the following statements—where <code>things</code> is an <a href="https://www.python.org/dev/peps/pep-0492/#asynchronous-iterators-and-async-for">asynchronous iterable</a>—are equivalent:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="pycon" data-is-repl="true">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect">
|
||
<span class="codeblock__output-toggle" title="Toggle prompts and output"><span class="icon baseline js-codeblock-output-on codeblock__header--icon-lower"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#regular--rectangle-terminal"></use></svg></span></span>
|
||
</div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="gp">>>> </span><span class="n">it</span> <span class="o">=</span> <span class="n">things</span><span class="o">.</span><span class="fm">__aiter__</span><span class="p">()</span>
|
||
<span class="gp">>>> </span><span class="n">it</span> <span class="o">=</span> <span class="nb">aiter</span><span class="p">(</span><span class="n">things</span><span class="p">)</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
In either case, <code>it</code> ends up as an asynchronous iterator. Expand the following box to see a complete example using <code>aiter()</code> and <code>anext()</code>:
|
||
</p>
|
||
<div class="card mb-3" id="collapse_card7ba5eb">
|
||
<div class="card-header border-0">
|
||
<p class="m-0">
|
||
<button class="btn w-100" data-toggle="collapse" data-target="#collapse7ba5eb" aria-expanded="false" aria-controls="collapse7ba5eb" markdown="1"><span class="float-left" markdown="1">Example using asynchronous iteration</span><span class="float-right text-muted">Show/Hide</span></button>
|
||
</p>
|
||
</div>
|
||
<div class="collapse js-collapsible-section" data-parent="#collapse_card7ba5eb" id="collapse7ba5eb">
|
||
<div class="card-body">
|
||
<p>
|
||
The following program counts the number of lines in several files. In practice, you use Python’s ability to iterate over files to count the number of lines. The script uses asynchronous iteration in order to handle several files concurrently.
|
||
</p>
|
||
<p>
|
||
Note that you need to install the third-party <a href="https://pypi.org/project/aiofiles/"><code>aiofiles</code></a> package with <a href="https://realpython.com/what-is-pip/"><code>pip</code></a> before running this code:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="c1"># line_count.py</span>
|
||
|
||
<span class="kn">import</span> <span class="nn">asyncio</span>
|
||
<span class="kn">import</span> <span class="nn">sys</span>
|
||
<span class="kn">import</span> <span class="nn">aiofiles</span>
|
||
|
||
<span class="k">async</span> <span class="k">def</span> <span class="nf">count_lines</span><span class="p">(</span><span class="n">filename</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""Count the number of lines in the given file"""</span>
|
||
<span class="n">num_lines</span> <span class="o">=</span> <span class="mi">0</span>
|
||
|
||
<span class="k">async</span> <span class="k">with</span> <span class="n">aiofiles</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="n">mode</span><span class="o">=</span><span class="s2">"r"</span><span class="p">)</span> <span class="k">as</span> <span class="n">file</span><span class="p">:</span>
|
||
<span class="hll"> <span class="n">lines</span> <span class="o">=</span> <span class="nb">aiter</span><span class="p">(</span><span class="n">file</span><span class="p">)</span>
|
||
</span> <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="hll"> <span class="k">await</span> <span class="n">anext</span><span class="p">(</span><span class="n">lines</span><span class="p">)</span>
|
||
</span> <span class="n">num_lines</span> <span class="o">+=</span> <span class="mi">1</span>
|
||
<span class="k">except</span> <span class="ne">StopAsyncIteration</span><span class="p">:</span>
|
||
<span class="k">break</span>
|
||
|
||
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">filename</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">num_lines</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||
|
||
<span class="k">async</span> <span class="k">def</span> <span class="nf">count_all_files</span><span class="p">(</span><span class="n">filenames</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""Asynchronously count lines in all files"""</span>
|
||
<span class="n">tasks</span> <span class="o">=</span> <span class="p">[</span><span class="n">asyncio</span><span class="o">.</span><span class="n">create_task</span><span class="p">(</span><span class="n">count_lines</span><span class="p">(</span><span class="n">f</span><span class="p">))</span> <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">filenames</span><span class="p">]</span>
|
||
<span class="k">await</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">gather</span><span class="p">(</span><span class="o">*</span><span class="n">tasks</span><span class="p">)</span>
|
||
|
||
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">"__main__"</span><span class="p">:</span>
|
||
<span class="n">asyncio</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">count_all_files</span><span class="p">(</span><span class="n">filenames</span><span class="o">=</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">:]))</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
<code>asyncio</code> is used to create and run one asynchronous task per filename. <code>count_lines()</code> opens one file asynchronously and iterates through it using <code>aiter()</code> and <code>anext()</code> in order to count the number of lines.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
See <a href="https://www.python.org/dev/peps/pep-0525/">PEP 525</a> to learn more about asynchronous iteration.
|
||
</p>
|
||
<div>
|
||
<div class="rounded border border-light" style="display:block;position:relative;">
|
||
<div style="display:block;width:100%;padding-top:12.5%;"></div>
|
||
<div class="rpad rounded border" data-unit="8x1" style="position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;">
|
||
<a href="https://srv.realpython.net/click/54948072182/?c=30337084727&p=58946116052&r=44065" rel="nofollow" target="_blank"><img src="https://img.realpython.net/913fd28f526ec8aca047551bce4892b6" style="max-width: 100%; max-height: 100%; width: 100%;" /></a>
|
||
</div>
|
||
</div><a class="small text-muted" href="/account/join/" rel="nofollow"><span class="icon baseline mr-1"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#solid--circle-info"></use></svg></span>Remove ads</a>
|
||
</div>
|
||
</section>
|
||
<section class="section3">
|
||
<h3 id="context-manager-syntax">
|
||
Context Manager Syntax<a class="headerlink" href="#context-manager-syntax" title="Permanent link"></a>
|
||
</h3>
|
||
<p>
|
||
<a href="https://realpython.com/python-with-statement/">Context managers</a> are great for managing resources in your programs. Until recently, though, their syntax has included an uncommon wart. You <a href="https://www.python.org/dev/peps/pep-0617/#some-rules-are-not-actually-ll-1">haven’t been allowed</a> to use parentheses to break long <code>with</code> statements like this:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="k">with</span> <span class="p">(</span>
|
||
<span class="n">read_path</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="n">mode</span><span class="o">=</span><span class="s2">"r"</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="s2">"utf-8"</span><span class="p">)</span> <span class="k">as</span> <span class="n">read_file</span><span class="p">,</span>
|
||
<span class="n">write_path</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="n">mode</span><span class="o">=</span><span class="s2">"w"</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="s2">"utf-8"</span><span class="p">)</span> <span class="k">as</span> <span class="n">write_file</span><span class="p">,</span>
|
||
<span class="p">):</span>
|
||
<span class="o">...</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
In earlier versions of Python, this causes an <code>invalid syntax</code> error message. Instead, you need to use a backslash (<code>\</code>) if you want to control where you break your lines:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="k">with</span> <span class="n">read_path</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="n">mode</span><span class="o">=</span><span class="s2">"r"</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="s2">"utf-8"</span><span class="p">)</span> <span class="k">as</span> <span class="n">read_file</span><span class="p">,</span> \
|
||
<span class="n">write_path</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="n">mode</span><span class="o">=</span><span class="s2">"w"</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="s2">"utf-8"</span><span class="p">)</span> <span class="k">as</span> <span class="n">write_file</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
While <a href="https://realpython.com/python-program-structure/#explicit-line-continuation">explicit line continuation</a> with backslashes is possible in Python, PEP 8 <a href="https://www.python.org/dev/peps/pep-0008/#maximum-line-length">discourages it</a>. The <a href="https://black.readthedocs.io/">Black</a> formatting tool <a href="https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html">avoids</a> backslashes completely.
|
||
</p>
|
||
<p>
|
||
In Python 3.10, you’re now allowed to add parentheses around <code>with</code> statements to your heart’s content. Especially if you’re employing several context managers at once, like in the example above, this can help improve the readability of your code. Python’s <a href="https://docs.python.org/3.10/whatsnew/3.10.html#parenthesized-context-managers">documentation</a> shows a few other possibilities with this new syntax.
|
||
</p>
|
||
<p>
|
||
One small <strong>fun fact</strong>: parenthesized <code>with</code> statements actually work in version 3.9 of <a href="https://realpython.com/cpython-source-code-guide/">CPython</a>. Their implementation came almost for free with the introduction of the <a href="https://realpython.com/python39-new-features/#a-more-powerful-python-parser">PEG parser</a> in <a href="https://realpython.com/python39-new-features/">Python 3.9</a>. The reason that this is called a Python 3.10 feature is that using the PEG parser is voluntary in Python 3.9, while Python 3.9, with the old LL(1) parser, doesn’t support parenthesized <code>with</code> statements.
|
||
</p>
|
||
</section>
|
||
<section class="section3">
|
||
<h3 id="modern-and-secure-ssl">
|
||
Modern and Secure SSL<a class="headerlink" href="#modern-and-secure-ssl" title="Permanent link"></a>
|
||
</h3>
|
||
<p>
|
||
Security can be challenging! A good rule of thumb is to avoid rolling your own security algorithms and instead rely on established packages.
|
||
</p>
|
||
<p>
|
||
Python uses <a href="https://www.openssl.org/">OpenSSL</a> for different cryptographic features that are exposed in the <a href="https://docs.python.org/3/library/hashlib.html"><code>hashlib</code></a>, <a href="https://docs.python.org/3/library/hmac.html"><code>hmac</code></a>, and <a href="https://docs.python.org/3/library/ssl.html"><code>ssl</code></a> standard library modules. Your system can manage OpenSSL, or a Python installer can include OpenSSL.
|
||
</p>
|
||
<p>
|
||
Python 3.9 supports using any of the <a href="https://en.wikipedia.org/wiki/OpenSSL#Major_version_releases">OpenSSL versions</a> 1.0.2 LTS, 1.1.0, and 1.1.1 LTS. Both OpenSSL 1.0.2 LTS and OpenSSL 1.1.0 are past their lifetime, so Python 3.10 will only support OpenSSL 1.1.1 LTS, as described in the following table:
|
||
</p>
|
||
<div class="table-responsive">
|
||
<table class="table table-hover">
|
||
<thead>
|
||
<tr>
|
||
<th>
|
||
Open SSL version
|
||
</th>
|
||
<th class="text-center">
|
||
Python 3.9
|
||
</th>
|
||
<th class="text-center">
|
||
Python 3.10
|
||
</th>
|
||
<th>
|
||
End-of-life
|
||
</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>
|
||
1.0.2 LTS
|
||
</td>
|
||
<td class="text-center">
|
||
✔
|
||
</td>
|
||
<td class="text-center">
|
||
✖
|
||
</td>
|
||
<td>
|
||
December 20, 2019
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td>
|
||
1.1.0
|
||
</td>
|
||
<td class="text-center">
|
||
✔
|
||
</td>
|
||
<td class="text-center">
|
||
✖
|
||
</td>
|
||
<td>
|
||
September 10, 2019
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td>
|
||
1.1.1 LTS
|
||
</td>
|
||
<td class="text-center">
|
||
✔
|
||
</td>
|
||
<td class="text-center">
|
||
✔
|
||
</td>
|
||
<td>
|
||
September 11, 2023
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<p>
|
||
This end of support for older versions will only affect you if you need to upgrade the system Python on an older operating system. If you use macOS or Windows, or if you install Python from <a href="https://www.python.org/">python.org</a> or use <a href="https://docs.conda.io/projects/conda/en/latest/user-guide/install/index.html">(Ana)Conda</a>, you’ll see no change.
|
||
</p>
|
||
<p>
|
||
However, <a href="https://ubuntu.com/">Ubuntu</a> 18.04 LTS uses OpenSSL 1.1.0, while <a href="https://www.redhat.com/en/technologies/linux-platforms/enterprise-linux">Red Hat Enterprise Linux</a> (RHEL) 7 and <a href="https://www.centos.org/">CentOS</a> 7 both use OpenSSL 1.0.2 LTS. If you need to run Python 3.10 on these systems, you should look at installing it yourself using either the <a href="https://www.python.org">python.org</a> or Conda installer.
|
||
</p>
|
||
<p>
|
||
Dropping support for older versions of OpenSSL will make Python more secure. It’ll also help the Python developers in that code will be easier to maintain. Ultimately, this helps you because your Python experience will be more robust. See <a href="https://www.python.org/dev/peps/pep-0644/">PEP 644</a> for more details.
|
||
</p>
|
||
</section>
|
||
<section class="section3">
|
||
<h3 id="more-information-about-your-python-interpreter">
|
||
More Information About Your Python Interpreter<a class="headerlink" href="#more-information-about-your-python-interpreter" title="Permanent link"></a>
|
||
</h3>
|
||
<p>
|
||
The <a href="https://docs.python.org/3/library/sys.html"><code>sys</code></a> module contains a lot of information about your system, the current Python runtime, and the script currently being executed. You can, for example, inquire about the paths where <a href="https://realpython.com/python-import/#pythons-import-path">Python looks for modules</a> with <a href="https://docs.python.org/3/library/sys.html#sys.path"><code>sys.path</code></a> and see all modules that <a href="https://realpython.com/python-import/#import-internals">have been imported</a> in the current session with <a href="https://docs.python.org/3/library/sys.html#sys.modules"><code>sys.modules</code></a>.
|
||
</p>
|
||
<p>
|
||
In Python 3.10, <code>sys</code> has two new attributes. First, you can now get a list of the names of all modules in the standard library:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="pycon" data-is-repl="true">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect">
|
||
<span class="codeblock__output-toggle" title="Toggle prompts and output"><span class="icon baseline js-codeblock-output-on codeblock__header--icon-lower"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#regular--rectangle-terminal"></use></svg></span></span>
|
||
</div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="gp">>>> </span><span class="kn">import</span> <span class="nn">sys</span>
|
||
|
||
<span class="gp">>>> </span><span class="nb">len</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">stdlib_module_names</span><span class="p">)</span>
|
||
<span class="go">302</span>
|
||
|
||
<span class="gp">>>> </span><span class="nb">sorted</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">stdlib_module_names</span><span class="p">)[</span><span class="o">-</span><span class="mi">5</span><span class="p">:]</span>
|
||
<span class="go">['zipapp', 'zipfile', 'zipimport', 'zlib', 'zoneinfo']</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
Here, you can see that there are around 300 modules in the standard library, several of which start with the letter <code>z</code>. Note that only top-level modules and packages are listed. Subpackages like <a href="https://realpython.com/python38-new-features/#importlibmetadata"><code>importlib.metadata</code></a> don’t get a separate entry.
|
||
</p>
|
||
<p>
|
||
You will probably not be using <a href="https://docs.python.org/3.10/library/sys.html#sys.stdlib_module_names"><code>sys.stdlib_module_names</code></a> all that often. Still, the list ties in nicely with similar introspection features like <a href="https://docs.python.org/3/library/keyword.html#keyword.kwlist"><code>keyword.kwlist</code></a> and <a href="https://docs.python.org/3/library/sys.html#sys.builtin_module_names"><code>sys.builtin_module_names</code></a>.
|
||
</p>
|
||
<p>
|
||
One possible use case for the new attribute is to identify which of the currently imported modules are third-party dependencies:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="pycon" data-is-repl="true">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect">
|
||
<span class="codeblock__output-toggle" title="Toggle prompts and output"><span class="icon baseline js-codeblock-output-on codeblock__header--icon-lower"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#regular--rectangle-terminal"></use></svg></span></span>
|
||
</div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="gp">>>> </span><span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="nn">pd</span>
|
||
<span class="gp">>>> </span><span class="kn">import</span> <span class="nn">sys</span>
|
||
|
||
<span class="gp">>>> </span><span class="p">{</span><span class="n">m</span> <span class="k">for</span> <span class="n">m</span> <span class="ow">in</span> <span class="n">sys</span><span class="o">.</span><span class="n">modules</span> <span class="k">if</span> <span class="s2">"."</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">m</span><span class="p">}</span> <span class="o">-</span> <span class="n">sys</span><span class="o">.</span><span class="n">stdlib_module_names</span>
|
||
<span class="go">{'__main__', 'numpy', '_cython_0_29_24', 'dateutil', 'pytz',</span>
|
||
<span class="go"> 'six', 'pandas', 'cython_runtime'}</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
You find the imported top-level modules by looking at names in <code>sys.modules</code> that don’t have a dot in their name. By comparing them to the standard library module names, you find that <a href="https://realpython.com/numpy-array-programming/"><code>numpy</code></a>, <a href="https://realpython.com/python-packages/#dateutil-for-working-with-dates-and-times"><code>dateutil</code></a>, and <a href="https://realpython.com/python-pandas-tricks/"><code>pandas</code></a> are some of the imported third-party modules in this example.
|
||
</p>
|
||
<p>
|
||
The other new attribute is <a href="https://docs.python.org/3.10/library/sys.html#sys.orig_argv"><code>sys.orig_argv</code></a>. This is related to <a href="https://docs.python.org/3/library/sys.html#sys.argv"><code>sys.argv</code></a>, which holds the <a href="https://realpython.com/python-command-line-arguments/#the-sysargv-array">command-line arguments</a> given to your program when it was started. In contrast, <code>sys.orig_argv</code> lists the command-line arguments passed to the <code>python</code> executable itself. Consider the following example:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="c1"># argvs.py</span>
|
||
|
||
<span class="kn">import</span> <span class="nn">sys</span>
|
||
|
||
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"argv: </span><span class="si">{</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"orig_argv: </span><span class="si">{</span><span class="n">sys</span><span class="o">.</span><span class="n">orig_argv</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
This script echoes back the <code>orig_argv</code> and <code>argv</code> lists. Run it to see how the information is captured:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="console" data-is-repl="true">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--yellow">
|
||
<span class="mr-2 noselect" aria-label="Language">Shell</span>
|
||
<div class="noselect">
|
||
<span class="codeblock__output-toggle" title="Toggle prompts and output"><span class="icon baseline js-codeblock-output-on codeblock__header--icon-lower"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#regular--rectangle-terminal"></use></svg></span></span>
|
||
</div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="gp">$ </span>python<span class="w"> </span>-X<span class="w"> </span>utf8<span class="w"> </span>-O<span class="w"> </span>argvs.py<span class="w"> </span><span class="m">3</span>.10<span class="w"> </span>--upgrade
|
||
<span class="go">argv: ['argvs.py', '3.10', '--upgrade']</span>
|
||
<span class="go">orig_argv: ['python', '-X', 'utf8', '-O', 'argvs.py', '3.10', '--upgrade']</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
Essentially, all arguments—including the name of the Python executable—end up in <code>orig_argv</code>. This is in contrast to <code>argv</code>, which only contains the arguments that aren’t handled by <code>python</code> itself.
|
||
</p>
|
||
<p>
|
||
Again, this is not a feature that you’ll use a lot. If your program needs to concern itself with how it’s being run, you’re usually better off relying on information that’s already exposed instead of trying to parse this list. For example, you can choose to use the <a href="#stricter-zipping-of-sequences">strict <code>zip()</code> mode</a> only when your script is not running with the optimized flag, <code>-O</code>, like this:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="nb">list</span><span class="p">(</span><span class="nb">zip</span><span class="p">(</span><span class="n">names</span><span class="p">,</span> <span class="n">set_numbers</span><span class="p">,</span> <span class="n">num_pieces</span><span class="p">,</span> <span class="n">strict</span><span class="o">=</span><span class="n">__debug__</span><span class="p">))</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
The <a href="https://docs.python.org/3/library/constants.html#__debug__"><code>__debug__</code></a> flag is set when the interpreter starts. It’ll be <code>False</code> if you’re running <code>python</code> with <a href="https://docs.python.org/3/using/cmdline.html#cmdoption-o"><code>-O</code></a> or <a href="https://docs.python.org/3/using/cmdline.html#cmdoption-oo"><code>-OO</code></a> specified, and <code>True</code> otherwise. Using <code>__debug__</code> is usually preferable to <code>"-O" not in sys.orig_argv</code> or some similar construct.
|
||
</p>
|
||
<p>
|
||
One of the <a href="https://bugs.python.org/issue23427#msg371028">motivating use cases</a> for <code>sys.orig_argv</code> is that you can use it to spawn a new Python process with the same or modified command-line arguments as your current process.
|
||
</p>
|
||
<div>
|
||
<div class="rounded border border-light" style="display:block;position:relative;">
|
||
<div style="display:block;width:100%;padding-top:12.5%;"></div>
|
||
<div class="rpad rounded border" data-unit="8x1" style="position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;">
|
||
<a href="https://srv.realpython.net/click/28266526380/?c=30337084727&p=58946116052&r=17741" rel="nofollow" target="_blank"><img src="https://img.realpython.net/e8b8e877da2a454b3a6930fecd28c95c" style="max-width: 100%; max-height: 100%; width: 100%;" /></a>
|
||
</div>
|
||
</div><a class="small text-muted" href="/account/join/" rel="nofollow"><span class="icon baseline mr-1"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#solid--circle-info"></use></svg></span>Remove ads</a>
|
||
</div>
|
||
</section>
|
||
<section class="section3">
|
||
<h3 id="future-annotations">
|
||
Future Annotations<a class="headerlink" href="#future-annotations" title="Permanent link"></a>
|
||
</h3>
|
||
<p>
|
||
<a href="https://realpython.com/python-type-checking/#annotations">Annotations</a> were introduced in Python 3 to give you a way to attach metadata to variables, function parameters, and return values. They are most commonly used to add type hints to your code.
|
||
</p>
|
||
<p>
|
||
One challenge with annotations is that they must be valid Python code. For one thing, this makes it <a href="https://realpython.com/python37-new-features/#typing-enhancements">hard to type hint</a> recursive classes. <a href="https://www.python.org/dev/peps/pep-0563/">PEP 563</a> introduced <a href="https://realpython.com/python-news-april-2021/#pep-563-pep-649-and-the-future-of-python-type-annotations">postponed evaluation of annotations</a>, making it possible to annotate with names that haven’t yet been defined. Since Python 3.7, you can activate postponed evaluation of annotations with a <a href="https://docs.python.org/3/library/__future__.html"><code>__future__</code></a> import:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">annotations</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
The intention was that postponed evaluation would become the default at some point in the future. After the <a href="https://pyfound.blogspot.com/2020/04/the-2020-python-language-summit.html">2020 Python Language Summit</a>, it was decided to make this happen in Python 3.10.
|
||
</p>
|
||
<p>
|
||
However, after more testing, it became clear that postponed evaluation didn’t work well for projects that use annotations at runtime. Key people in the <a href="https://realpython.com/fastapi-python-web-apis/">FastAPI</a> and the <a href="https://pydantic-docs.helpmanual.io/">Pydantic</a> projects <a href="https://dev.to/tiangolo/the-future-of-fastapi-and-pydantic-is-bright-3pbm">voiced their concerns</a>. At the last minute, it was decided to reschedule these changes for Python 3.11.
|
||
</p>
|
||
<p>
|
||
To ease the transition into future behavior, a few changes have been made in Python 3.10 as well. Most importantly, a new <a href="https://docs.python.org/3.10/library/inspect.html#inspect.get_annotations"><code>inspect.get_annotations()</code></a> function has been added. You should call this to access annotations at runtime:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="pycon" data-is-repl="true">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect">
|
||
<span class="codeblock__output-toggle" title="Toggle prompts and output"><span class="icon baseline js-codeblock-output-on codeblock__header--icon-lower"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#regular--rectangle-terminal"></use></svg></span></span>
|
||
</div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="gp">>>> </span><span class="kn">import</span> <span class="nn">inspect</span>
|
||
|
||
<span class="gp">>>> </span><span class="k">def</span> <span class="nf">mean</span><span class="p">(</span><span class="n">numbers</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span> <span class="o">|</span> <span class="nb">float</span><span class="p">])</span> <span class="o">-></span> <span class="nb">float</span><span class="p">:</span>
|
||
<span class="gp">... </span> <span class="k">return</span> <span class="nb">sum</span><span class="p">(</span><span class="n">numbers</span><span class="p">)</span> <span class="o">/</span> <span class="nb">len</span><span class="p">(</span><span class="n">numbers</span><span class="p">)</span>
|
||
<span class="gp">...</span>
|
||
|
||
<span class="gp">>>> </span><span class="n">inspect</span><span class="o">.</span><span class="n">get_annotations</span><span class="p">(</span><span class="n">mean</span><span class="p">)</span>
|
||
<span class="go">{'numbers': list[int | float], 'return': <class 'float'>}</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
Check out <a href="https://docs.python.org/3.10/howto/annotations.html">Annotations Best Practices</a> for details.
|
||
</p>
|
||
</section>
|
||
</section>
|
||
<section class="section2">
|
||
<h2 id="how-to-detect-python-310-at-runtime">
|
||
How to Detect Python 3.10 at Runtime<a class="headerlink" href="#how-to-detect-python-310-at-runtime" title="Permanent link"></a>
|
||
</h2>
|
||
<p>
|
||
Python 3.10 is the first version of Python with a two-digit minor version number. While this is mostly an interesting fun fact and an indication that Python 3 has been around for quite some time, it does also have some practical consequences.
|
||
</p>
|
||
<p>
|
||
When your code needs to do something specific based on the version of Python at runtime, you’ve gotten away with doing a <a href="https://docs.python.org/3/reference/expressions.html#value-comparisons">lexicographical</a> comparison of version strings until now. While it’s never been good practice, it’s been possible to do the following:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="c1"># bad_version_check.py</span>
|
||
|
||
<span class="kn">import</span> <span class="nn">sys</span>
|
||
|
||
<span class="c1"># Don't do the following</span>
|
||
<span class="k">if</span> <span class="n">sys</span><span class="o">.</span><span class="n">version</span> <span class="o"><</span> <span class="s2">"3.6"</span><span class="p">:</span>
|
||
<span class="k">raise</span> <span class="ne">SystemExit</span><span class="p">(</span><span class="s2">"Only Python 3.6 and above is supported"</span><span class="p">)</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
In Python 3.10, this code will raise <code>SystemExit</code> and stop your program. This happens because, as strings, <code>"3.10"</code> is less than <code>"3.6"</code>.
|
||
</p>
|
||
<p>
|
||
The correct way to compare version numbers is to use tuples of numbers:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="python">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--blue">
|
||
<span class="mr-2 noselect" aria-label="Language">Python</span>
|
||
<div class="noselect"></div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="c1"># good_version_check.py</span>
|
||
|
||
<span class="kn">import</span> <span class="nn">sys</span>
|
||
|
||
<span class="k">if</span> <span class="n">sys</span><span class="o">.</span><span class="n">version_info</span> <span class="o"><</span> <span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">6</span><span class="p">):</span>
|
||
<span class="k">raise</span> <span class="ne">SystemExit</span><span class="p">(</span><span class="s2">"Only Python 3.6 and above is supported"</span><span class="p">)</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
<a href="https://docs.python.org/3/library/sys.html#sys.version_info"><code>sys.version_info</code></a> is a tuple object you can use for comparisons.
|
||
</p>
|
||
<p>
|
||
If you’re doing these kinds of comparisons in your code, you should check your code with <a href="https://pypi.org/project/flake8-2020/">flake8-2020</a> to make sure you’re handling versions correctly:
|
||
</p>
|
||
<div class="codeblock mb-3 w-100" aria-label="Code block" data-syntax-language="console" data-is-repl="true">
|
||
<div class="codeblock__header d-flex justify-content-between codeblock--yellow">
|
||
<span class="mr-2 noselect" aria-label="Language">Shell</span>
|
||
<div class="noselect">
|
||
<span class="codeblock__output-toggle" title="Toggle prompts and output"><span class="icon baseline js-codeblock-output-on codeblock__header--icon-lower"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#regular--rectangle-terminal"></use></svg></span></span>
|
||
</div>
|
||
</div>
|
||
<div style="position: relative;">
|
||
<div class="highlight highlight--with-header">
|
||
<pre><code><span class="gp">$ </span>python<span class="w"> </span>-m<span class="w"> </span>pip<span class="w"> </span>install<span class="w"> </span>flake8-2020
|
||
|
||
<span class="gp">$ </span>flake8<span class="w"> </span>bad_version_check.py<span class="w"> </span>good_version_check.py
|
||
<span class="go">bad_version_check.py:3:4: YTT103 `sys.version` compared to string</span>
|
||
<span class="go"> (python3.10), use `sys.version_info`</span>
|
||
</code></pre>
|
||
</div><button class="codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only" title="Copy to clipboard"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@copy"></use></svg></span></button>
|
||
<template class="codeblock__copied-template">
|
||
<span class="small"><span class="icon baseline mr-1 text-success"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#@check"></use></svg></span>Copied!</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
With the <code>flake8-2020</code> extension activated, you’ll get a recommendation about replacing <code>sys.version</code> with <code>sys.version_info</code>.
|
||
</p>
|
||
<div>
|
||
<div class="rounded border border-light" style="display:block;position:relative;">
|
||
<div style="display:block;width:100%;padding-top:12.5%;"></div>
|
||
<div class="rpad rounded border" data-unit="8x1" style="position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;">
|
||
<a href="https://srv.realpython.net/click/36075076065/?c=30337084727&p=58946116052&r=16765" rel="nofollow" target="_blank"><img src="https://img.realpython.net/fc3dcce3b30783158d89077cf5a8810e" style="max-width: 100%; max-height: 100%; width: 100%;" /></a>
|
||
</div>
|
||
</div><a class="small text-muted" href="/account/join/" rel="nofollow"><span class="icon baseline mr-1"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#solid--circle-info"></use></svg></span>Remove ads</a>
|
||
</div>
|
||
</section>
|
||
<section class="section2">
|
||
<h2 id="so-should-you-upgrade-to-python-310">
|
||
So, Should You Upgrade to Python 3.10?<a class="headerlink" href="#so-should-you-upgrade-to-python-310" title="Permanent link"></a>
|
||
</h2>
|
||
<p>
|
||
You’ve now seen the coolest features of the newest and latest version of Python. The question now is whether you should upgrade to Python 3.10, and if yes, when you should do so. There are two different aspects to consider when thinking about upgrading to Python 3.10:
|
||
</p>
|
||
<ol>
|
||
<li>Should you upgrade your environment so that you <strong>run your code</strong> with the Python 3.10 interpreter?
|
||
</li>
|
||
<li>Should you <strong>write your code</strong> using the new Python 3.10 features?
|
||
</li>
|
||
</ol>
|
||
<p>
|
||
Clearly, if you want to test out structural pattern matching or any of the other cool new features you’ve read about here, you need Python 3.10. It’s possible to install the latest version side by side with your current Python version. A straightforward way to do this is to use an environment manager like <a href="https://realpython.com/intro-to-pyenv/">pyenv</a> or <a href="https://realpython.com/python-windows-machine-learning-setup/">Conda</a>. You can also <a href="https://realpython.com/python-versions-docker/">use Docker</a> to run Python 3.10 without installing it locally.
|
||
</p>
|
||
<p>
|
||
Python 3.10 has been through about five months of beta testing, so there shouldn’t be any big issues with starting to use it for your own development. You may find that some of your dependencies don’t immediately have <a href="https://realpython.com/python-wheels/">wheels for Python 3.10</a> available, which makes them more cumbersome to install. But in general, using the newest Python for local development is fairly safe.
|
||
</p>
|
||
<p>
|
||
As always, you should be careful before upgrading your production environment. Be vigilant about testing that your code runs well on the new version. In particular, you want to be on the lookout for features that are <a href="https://docs.python.org/3.10/whatsnew/3.10.html#deprecated">deprecated</a> or <a href="https://docs.python.org/3.10/whatsnew/3.10.html#removed">removed</a>.
|
||
</p>
|
||
<p>
|
||
Whether you can start using the new features in your code or not depends on your user base and the environment where your code is running. If you can guarantee that Python 3.10 is available, then there’s no danger in using the new union type syntax or any other new feature.
|
||
</p>
|
||
<p>
|
||
If you’re distributing an app or a library that’s used by others instead, you may want to be a bit more conservative. Currently, <a href="https://www.python.org/dev/peps/pep-0494">Python 3.6</a> is the oldest officially supported Python version. It reaches end-of-life in December 2021, after which <a href="https://www.python.org/dev/peps/pep-0537">Python 3.7</a> will be the minimum supported version.
|
||
</p>
|
||
<p>
|
||
The documentation includes a useful guide about <a href="https://docs.python.org/3.10/whatsnew/3.10.html#porting-to-python-3-10">porting your code to Python 3.10</a>. Check it out for more details!
|
||
</p>
|
||
</section>
|
||
<section class="section2">
|
||
<h2 id="conclusion">
|
||
Conclusion<a class="headerlink" href="#conclusion" title="Permanent link"></a>
|
||
</h2>
|
||
<p>
|
||
The release of a new Python version is always worth celebrating. Even if you can’t start using the new features right away, they’ll become broadly available and part of your daily life within a few years.
|
||
</p>
|
||
<p>
|
||
<strong>In this tutorial, you’ve seen new features like:</strong>
|
||
</p>
|
||
<ul>
|
||
<li>Friendlier <strong>error messages</strong>
|
||
</li>
|
||
<li>Powerful <strong>structural pattern matching</strong>
|
||
</li>
|
||
<li>
|
||
<strong>Type hint</strong> improvements
|
||
</li>
|
||
<li>Safer <strong>combination of sequences</strong>
|
||
</li>
|
||
<li>New <strong>statistics functions</strong>
|
||
</li>
|
||
</ul>
|
||
<p>
|
||
For more Python 3.10 tips and a discussion with members of the <em>Real Python</em> team, check out <a href="https://realpython.com/podcasts/rpp/81/">Real Python Podcast Episode #81</a>.
|
||
</p>
|
||
<p>
|
||
Have fun trying out the new features! Share your experiences in the comments below.
|
||
</p>
|
||
</section>
|
||
<div class="text-center my-3">
|
||
<div class="jsCompletionStatusWidget btn-group mb-0">
|
||
<button title="Click to mark as completed" class="jsBtnCompletion btn btn-secondary border-right" style="border-top-right-radius: 0; border-bottom-right-radius: 0;">Mark as Completed</button> <button title="Add bookmark" class="jsBtnBookmark btn btn-secondary border-left"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#light--bookmark"></use></svg></span></button>
|
||
</div>
|
||
<div class="btn-group mb-0">
|
||
<span class="ml-2"><a class="btn btn-secondary border-right" style="border-top-right-radius: 0; border-bottom-right-radius: 0;" title="Liked it" role="button" aria-label="Thumbs up (liked it)" href="/feedback/survey/article/python310-new-features/liked/?from=article-footer" target="_blank"><span class="icon"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#light--thumbs-up"></use></svg></span></a> <a class="btn btn-secondary border-left" role="button" aria-label="Thumbs down (disliked it)" title="Disliked it" href="/feedback/survey/article/python310-new-features/disliked/?from=article-footer" target="_blank"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#light--thumbs-down"></use></svg></span></a></span>
|
||
</div>
|
||
</div>
|
||
<div class="border rounded p-3 card mb-2">
|
||
<p class="mb-0">
|
||
<span class="badge badge-pill badge-success"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#v4--play-circle"></use></svg></span> Watch Now</span> This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: <a class="stretched-link text-success" href="/courses/cool-new-features-python-310/"><strong>Cool New Features in Python 3.10</strong></a>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<div class="card mt-4 mb-4 bg-secondary">
|
||
<p class="card-header h3 text-center bg-light">
|
||
🐍 Python Tricks 💌
|
||
</p>
|
||
<div class="card-body">
|
||
<div class="container">
|
||
<div class="row">
|
||
<div class="col-xs-12 col-sm-7">
|
||
<p>
|
||
Get a short & sweet <strong>Python Trick</strong> delivered to your inbox every couple of days. No spam ever. Unsubscribe any time. Curated by the Real Python team.
|
||
</p>
|
||
</div>
|
||
<div class="col-xs-12 col-sm-5">
|
||
<img loading="lazy" class="img-fluid rounded mb-3" src="/static/pytrick-dict-merge.4201a0125a5e.png" width="738" height="490" alt="Python Tricks Dictionary Merge" />
|
||
</div>
|
||
</div>
|
||
<div class="row mb-3">
|
||
<form class="col-12" action="/optins/process/" method="post">
|
||
<input type="hidden" name="csrfmiddlewaretoken" value="uS9jW4J1OA6NsjWe9e3lAb0KuP7EYOUAGX3WH46iyCnwy1uBqPKTipw66s5mt7Yj" /> <input type="hidden" name="slug" value="static-python-tricks-footer" />
|
||
<div class="form-group">
|
||
<input name="email" type="email" class="form-control form-control-lg" placeholder="Email Address" required="" />
|
||
</div><button name="submit" type="submit" class="btn btn-primary btn-lg btn-block">Send Me Python Tricks »</button>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="card mt-3" id="author">
|
||
<p class="card-header h3">
|
||
About <strong>Geir Arne Hjelle</strong>
|
||
</p>
|
||
<div class="card-body">
|
||
<div class="container p-0">
|
||
<div class="row">
|
||
<div class="col-12 col-md-3 align-self-center">
|
||
<a href="/team/gahjelle/"><img loading="lazy" src="https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/gahjelle.470149ee709e.jpg&w=800&h=800&mode=crop&sig=e9b761c6cf1359953014dba05554f5424eb116e1" srcset="https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/gahjelle.470149ee709e.jpg&w=200&h=200&mode=crop&sig=c6390201e73d3e09429d73da5bb29c17ab10403a 200w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/gahjelle.470149ee709e.jpg&w=266&h=266&mode=crop&sig=7df7c39b123c3f9d8a4597311d09c3bd947f5fac 266w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/gahjelle.470149ee709e.jpg&w=400&h=400&mode=crop&sig=fcea459ee24a7b320573cadee324cf75509dc1d6 400w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/gahjelle.470149ee709e.jpg&w=800&h=800&mode=crop&sig=e9b761c6cf1359953014dba05554f5424eb116e1 800w" sizes="(min-width: 580px) 154px, calc(33.08vw - 24px)" width="800" height="800" style="background: #080a09;" class="d-block d-md-none rounded-circle img-fluid w-33 mb-0 mx-auto" alt="Geir Arne Hjelle" /></a> <a href="/team/gahjelle/"><img loading="lazy" src="https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/gahjelle.470149ee709e.jpg&w=800&h=800&mode=crop&sig=e9b761c6cf1359953014dba05554f5424eb116e1" srcset="https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/gahjelle.470149ee709e.jpg&w=200&h=200&mode=crop&sig=c6390201e73d3e09429d73da5bb29c17ab10403a 200w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/gahjelle.470149ee709e.jpg&w=266&h=266&mode=crop&sig=7df7c39b123c3f9d8a4597311d09c3bd947f5fac 266w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/gahjelle.470149ee709e.jpg&w=400&h=400&mode=crop&sig=fcea459ee24a7b320573cadee324cf75509dc1d6 400w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/gahjelle.470149ee709e.jpg&w=800&h=800&mode=crop&sig=e9b761c6cf1359953014dba05554f5424eb116e1 800w" sizes="(min-width: 1200px) 140px, calc(-1.5vw + 137px)" width="800" height="800" style="background: #080a09;" class="d-none d-md-block rounded-circle img-fluid w-100 mb-0" alt="Geir Arne Hjelle" /></a>
|
||
</div>
|
||
<div class="col mt-3">
|
||
<p>
|
||
Geir Arne is an avid Pythonista and a member of the Real Python tutorial team.
|
||
</p><a href="/team/gahjelle/" class="card-link">» More about Geir Arne</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<hr class="my-0" />
|
||
<div class="card-body pb-0">
|
||
<div class="container">
|
||
<div class="row">
|
||
<p>
|
||
<em>Each tutorial at Real Python is created by a team of developers so that it meets our high quality standards. The team members who worked on this tutorial are:</em>
|
||
</p>
|
||
</div>
|
||
<div class="row align-items-center w-100 mx-auto">
|
||
<div class="col-4 col-sm-2 align-self-center">
|
||
<a href="/team/asantos/"><img loading="lazy" src="https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/PP.9b8b026f75b8.jpg&w=959&h=959&mode=crop&sig=70bedc2eab90a227eb9a657c415689c3eb1eca4f" srcset="https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/PP.9b8b026f75b8.jpg&w=239&h=239&mode=crop&sig=11667a6dd5c29e4c9363f18be59360551af5eddc 239w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/PP.9b8b026f75b8.jpg&w=319&h=319&mode=crop&sig=3b626fde72a314418766c7d4a14e58ec6f67da7b 319w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/PP.9b8b026f75b8.jpg&w=479&h=479&mode=crop&sig=1541e1ec541357813def826d8507c0565164b701 479w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/PP.9b8b026f75b8.jpg&w=959&h=959&mode=crop&sig=70bedc2eab90a227eb9a657c415689c3eb1eca4f 959w" sizes="(min-width: 1200px) 73px, (min-width: 780px) calc(-0.75vw + 69px), (min-width: 580px) 43px, calc(33.46vw - 64px)" width="959" height="959" style="background: #dadad8;" class="rounded-circle img-fluid w-100" alt="Aldren Santos" /></a>
|
||
</div>
|
||
<div class="col pl-0 d-none d-sm-block">
|
||
<a href="/team/asantos/" class="card-link small">
|
||
<p>
|
||
Aldren
|
||
</p></a>
|
||
</div>
|
||
<div class="col-4 col-sm-2 align-self-center">
|
||
<a href="/team/ctrudeau/"><img loading="lazy" src="https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/headshot_2018_x500_sharpen_compress.198bfb04d7f9.JPG&w=500&h=500&mode=crop&sig=96b4bd41ff3f90d880a4ab5acbbe43eaff81b1e5" srcset="https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/headshot_2018_x500_sharpen_compress.198bfb04d7f9.JPG&w=125&h=125&mode=crop&sig=c4c75cb8adbd6599d0712f39bb5b4d941952d04b 125w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/headshot_2018_x500_sharpen_compress.198bfb04d7f9.JPG&w=166&h=166&mode=crop&sig=844a02dc8941ac03234974a145c932b5d086ac16 166w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/headshot_2018_x500_sharpen_compress.198bfb04d7f9.JPG&w=250&h=250&mode=crop&sig=7f3333d3963c2ad741f41618a51e24e14ce2952b 250w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/headshot_2018_x500_sharpen_compress.198bfb04d7f9.JPG&w=500&h=500&mode=crop&sig=96b4bd41ff3f90d880a4ab5acbbe43eaff81b1e5 500w" sizes="(min-width: 1200px) 73px, (min-width: 780px) calc(-0.75vw + 69px), (min-width: 580px) 43px, calc(33.46vw - 64px)" width="500" height="500" style="background: #402c21;" class="rounded-circle img-fluid w-100" alt="Christopher Trudeau" /></a>
|
||
</div>
|
||
<div class="col pl-0 d-none d-sm-block">
|
||
<a href="/team/ctrudeau/" class="card-link small">
|
||
<p>
|
||
Christopher
|
||
</p></a>
|
||
</div>
|
||
<div class="col-4 col-sm-2 align-self-center">
|
||
<a href="/team/damos/"><img loading="lazy" src="https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/me-small.f5f49f1c48e1.jpg&w=400&h=400&mode=crop&sig=522ebc378492fd2228f8a9980bf89544b51aba2c" srcset="https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/me-small.f5f49f1c48e1.jpg&w=100&h=100&mode=crop&sig=4e832705873c990ccc3fe2459dcd2b081613ecb1 100w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/me-small.f5f49f1c48e1.jpg&w=133&h=133&mode=crop&sig=fa76af48f5292f75f931d2dc0eb223cc49113518 133w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/me-small.f5f49f1c48e1.jpg&w=200&h=200&mode=crop&sig=ecc773a7a56114ed2831d281156669f0d7f1ab68 200w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/me-small.f5f49f1c48e1.jpg&w=400&h=400&mode=crop&sig=522ebc378492fd2228f8a9980bf89544b51aba2c 400w" sizes="(min-width: 1200px) 73px, (min-width: 780px) calc(-0.75vw + 69px), (min-width: 580px) 43px, calc(33.46vw - 64px)" width="400" height="400" style="background: #7c7e7d;" class="rounded-circle img-fluid w-100" alt="David Amos" /></a>
|
||
</div>
|
||
<div class="col pl-0 d-none d-sm-block">
|
||
<a href="/team/damos/" class="card-link small">
|
||
<p>
|
||
David
|
||
</p></a>
|
||
</div>
|
||
</div>
|
||
<div class="row align-items-center w-100 mx-auto">
|
||
<div class="col-4 col-sm-2 align-self-center">
|
||
<a href="/team/dbader/"><img loading="lazy" src="https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/daniel-square.d58bf4388750.jpg&w=1000&h=1000&mode=crop&sig=304f5f568993310d5e87b2ca3c504260c018effa" srcset="https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/daniel-square.d58bf4388750.jpg&w=250&h=250&mode=crop&sig=981634b4528584f2e9c7ee663477173599e1781a 250w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/daniel-square.d58bf4388750.jpg&w=333&h=333&mode=crop&sig=96005b865a7753e3bd47fd88dafab70a686b1224 333w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/daniel-square.d58bf4388750.jpg&w=500&h=500&mode=crop&sig=841025b97d25f05c1b90802032e477020462fe01 500w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/daniel-square.d58bf4388750.jpg&w=1000&h=1000&mode=crop&sig=304f5f568993310d5e87b2ca3c504260c018effa 1000w" sizes="(min-width: 1200px) 73px, (min-width: 780px) calc(-0.75vw + 69px), (min-width: 580px) 43px, calc(33.46vw - 64px)" width="1000" height="1000" style="background: #dad5cf;" class="rounded-circle img-fluid w-100" alt="Dan Bader" /></a>
|
||
</div>
|
||
<div class="col pl-0 d-none d-sm-block">
|
||
<a href="/team/dbader/" class="card-link small">
|
||
<p>
|
||
Dan
|
||
</p></a>
|
||
</div>
|
||
<div class="col-4 col-sm-2 align-self-center">
|
||
<a href="/team/sparker/"><img loading="lazy" src="https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/profpic_sp.a008488b6af0.jpeg&w=800&h=800&mode=crop&sig=ec9f8b060e53c9b35339584491f286fd52c11452" srcset="https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/profpic_sp.a008488b6af0.jpeg&w=200&h=200&mode=crop&sig=ce5407d34a490d4daf29ad57e74fd3870dd28774 200w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/profpic_sp.a008488b6af0.jpeg&w=266&h=266&mode=crop&sig=f9684a52e8fabcc7dd3f88e96321df221ac1723a 266w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/profpic_sp.a008488b6af0.jpeg&w=400&h=400&mode=crop&sig=dfd70d0dde444f036864e5cc145df9b18fbae649 400w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/profpic_sp.a008488b6af0.jpeg&w=800&h=800&mode=crop&sig=ec9f8b060e53c9b35339584491f286fd52c11452 800w" sizes="(min-width: 1200px) 73px, (min-width: 780px) calc(-0.75vw + 69px), (min-width: 580px) 43px, calc(33.46vw - 64px)" width="800" height="800" style="background: #c5b2a3;" class="rounded-circle img-fluid w-100" alt="Sadie Parker" /></a>
|
||
</div>
|
||
<div class="col pl-0 d-none d-sm-block">
|
||
<a href="/team/sparker/" class="card-link small">
|
||
<p>
|
||
Sadie
|
||
</p></a>
|
||
</div>
|
||
<div class="col-4 col-sm-2 align-self-center"></div>
|
||
<div class="col pl-0 d-none d-sm-block"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="bg-light rounded py-4 my-4 shadow shadow-sm mx-n2">
|
||
<div class="col-12 text-center d-block d-md-none">
|
||
<p class="h2 mb-3">
|
||
Master <u><span class="marker-highlight">Real-World Python Skills</span></u> With Unlimited Access to Real Python
|
||
</p>
|
||
<p class="mb-1">
|
||
<img loading="lazy" class="w-75" src="/static/videos/lesson-locked.f5105cfd26db.svg" width="510" height="260" />
|
||
</p>
|
||
<p class="mx-auto w-75 mb-3 small">
|
||
<strong>Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:</strong>
|
||
</p>
|
||
<p class="mb-0">
|
||
<a href="/account/join/?utm_source=rp_article_footer&utm_content=python310-new-features" class="btn btn-primary btn-sm px-4 mb-0">Level Up Your Python Skills »</a>
|
||
</p>
|
||
</div>
|
||
<div class="col-12 text-center d-none d-md-block">
|
||
<p class="h2 mb-2">
|
||
Master <u><span class="marker-highlight">Real-World Python Skills</span></u><br />
|
||
With Unlimited Access to Real Python
|
||
</p>
|
||
<p class="mb-2">
|
||
<img loading="lazy" class="w-50 mb-2" src="/static/videos/lesson-locked.f5105cfd26db.svg" width="510" height="260" />
|
||
</p>
|
||
<p class="mx-auto w-50 mb-3">
|
||
<strong>Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:</strong>
|
||
</p>
|
||
<p>
|
||
<a href="/account/join/?utm_source=rp_article_footer&utm_content=python310-new-features" class="btn btn-primary btn-lg px-4">Level Up Your Python Skills »</a>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<div class="card mt-4" id="reader-comments">
|
||
<p class="card-header h3">
|
||
What Do You Think?
|
||
</p>
|
||
<div class="text-center mt-3 mb-0 p-0">
|
||
<div class="mb-2">
|
||
<strong class="mr-2">Rate this article:</strong>
|
||
<div class="btn-group mb-0">
|
||
<a class="btn btn-secondary border-right" style="border-top-right-radius: 0; border-bottom-right-radius: 0;" title="Liked it" role="button" aria-label="Thumbs up (liked it)" href="/feedback/survey/article/python310-new-features/liked/?from=article-comments" target="_blank"><span class="icon"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#light--thumbs-up"></use></svg></span></a> <a class="btn btn-secondary border-left" role="button" aria-label="Thumbs down (disliked it)" title="Disliked it" href="/feedback/survey/article/python310-new-features/disliked/?from=article-comments" target="_blank"><span class="icon baseline"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#light--thumbs-down"></use></svg></span></a>
|
||
</div>
|
||
</div>
|
||
<div class="text-center my-3">
|
||
<span><a target="_blank" rel="nofollow" href="https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Frealpython.com%2Fpython310-new-features%2F" class="mr-1 btn btn-sm badge-linkedin text-light mb-1"><span class="icon baseline mr-1 text-light"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#brands--linkedin"></use></svg></span>LinkedIn</a> <a target="_blank" rel="nofollow" href="https://twitter.com/intent/tweet/?text=Interesting%20Python%20article%20on%20%40realpython%3A%20Python%203.10%3A%20Cool%20New%20Features%20for%20You%20to%20Try&url=https%3A%2F%2Frealpython.com%2Fpython310-new-features%2F" class="mr-1 btn btn-sm badge-x-twitter text-light mb-1"><span class="icon baseline mr-1 text-light"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#brands--x-twitter"></use></svg></span>Twitter</a> <a target="_blank" rel="nofollow" href="https://facebook.com/sharer/sharer.php?u=https%3A%2F%2Frealpython.com%2Fpython310-new-features%2F" class="mr-1 btn btn-sm badge-facebook text-light mb-1"><span class="icon baseline mr-1 text-light"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#brands--facebook"></use></svg></span>Facebook</a> <a target="_blank" rel="nofollow" href="mailto:?subject=Python%20article%20for%20you&body=Python%203.10%3A%20Cool%20New%20Features%20for%20You%20to%20Try%20on%20Real%20Python%0A%0Ahttps%3A%2F%2Frealpython.com%2Fpython310-new-features%2F%0A" class="mr-1 btn btn-sm badge-dark text-light mb-1"><span class="icon baseline mr-1 text-light"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#solid--envelope"></use></svg></span>Email</a></span>
|
||
</div>
|
||
</div>
|
||
<div class="card-body">
|
||
<p>
|
||
What’s your #1 takeaway or favorite thing you learned? How are you going to put your newfound skills to use? Leave a comment below and let us know.
|
||
</p>
|
||
<div class="alert alert-dark">
|
||
<p class="mb-0">
|
||
<strong>Commenting Tips:</strong> The most useful comments are those written with the goal of learning from or helping out other students. <a href="https://realpython.com/python-beginner-tips/#tip-9-ask-good-questions" target="_blank">Get tips for asking good questions</a> and <a href="https://support.realpython.com" target="_blank">get answers to common questions in our support portal</a>.
|
||
</p>
|
||
<hr />
|
||
Looking for a real-time conversation? Visit the <a href="/community/" target="_blank">Real Python Community Chat</a> or join the next <a href="/office-hours/" target="_blank">“Office Hours” Live Q&A Session</a>. Happy Pythoning!
|
||
</div>
|
||
<div class="mb-4" id="disqus_thread"></div>
|
||
</div>
|
||
</div>
|
||
<div class="card mt-4 mb-4">
|
||
<p class="card-header h3">
|
||
Keep Learning
|
||
</p>
|
||
<div class="card-body">
|
||
<p class="mb-0">
|
||
Related Tutorial Categories: <a href="/tutorials/intermediate/" class="badge badge-light text-muted">intermediate</a> <a href="/tutorials/python/" class="badge badge-light text-muted">python</a>
|
||
</p>
|
||
<p class="mt-3 mb-0">
|
||
Recommended Video Course: <a class="text-success" href="/courses/cool-new-features-python-310/">Cool New Features in Python 3.10</a>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<div class="modal fade" tabindex="-1" role="dialog" id="rprw">
|
||
<div class="modal-dialog modal-lg modal-dialog-centered" role="document">
|
||
<div class="modal-content">
|
||
<div class="modal-header border-0">
|
||
<div class="col-12 col-lg-9 mx-auto modal-title text-center my-2">
|
||
<h2 class="my-0">
|
||
Keep reading Real Python by creating a free account or signing in:
|
||
</h2>
|
||
</div>
|
||
</div>
|
||
<div class="modal-body bg-light">
|
||
<div class="col-12 text-center">
|
||
<div class="col-12 col-lg-8 mx-auto mb-2 mt-3">
|
||
<a href="/account/signup/?intent=continue_reading&utm_source=rp&utm_medium=web&utm_campaign=rwn&utm_content=v1&next=%2Fpython310-new-features%2F"><img loading="lazy" class="w-100" src="/static/videos/lesson-locked.f5105cfd26db.svg" width="510" height="260" alt="Keep reading" /></a>
|
||
</div>
|
||
<p>
|
||
<a href="/account/signup/?intent=continue_reading&utm_source=rp&utm_medium=web&utm_campaign=rwn&utm_content=v1&next=%2Fpython310-new-features%2F" class="btn btn-primary btn-lg px-5">Continue »</a>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer border-0">
|
||
<p class="text-center text-muted mt-2 mb-1">
|
||
Already have an account? <a href="/account/login/?next=/python310-new-features/">Sign-In</a>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<script src="/static/frontend/reader/rw.bea76ad0e23f.js" async="async"></script>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="modal fade" id="modal-python-mastery-course" tabindex="-1" aria-labelledby="modal-python-mastery-course-title" aria-describedby="modal-python-mastery-course-desc">
|
||
<div class="modal-dialog modal-dialog-centered modal-lg">
|
||
<div class="modal-content">
|
||
<div class="modal-header bg-light pt-3 pb-2">
|
||
<div class="container-fluid">
|
||
<div class="row">
|
||
<div class="col-12">
|
||
<div class="progress" style="height: .5rem;" aria-hidden="true">
|
||
<div class="progress-bar progress-bar-striped progress-bar-animated w-75" role="progressbar"></div>
|
||
</div>
|
||
</div>
|
||
<div class="col-12">
|
||
<p id="modal-python-mastery-course-desc" class="text-muted text-center mb-0 mt-2">
|
||
Almost there! Complete this form and click the button below to gain instant access:
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div><button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||
</div>
|
||
<div class="modal-body m-4">
|
||
<div class="container-fluid">
|
||
<div class="row align-items-center text-center">
|
||
<div class="col-12 col-lg-4 mb-4 mb-lg-0">
|
||
<img loading="lazy" class="img-fluid rounded" src="https://files.realpython.com/media/python-logo.8eb72ea6927b.png" width="1000" height="1000" srcset="/cdn-cgi/image/width=250,format=auto/https://files.realpython.com/media/python-logo.8eb72ea6927b.png 250w, /cdn-cgi/image/width=333,format=auto/https://files.realpython.com/media/python-logo.8eb72ea6927b.png 333w, /cdn-cgi/image/width=500,format=auto/https://files.realpython.com/media/python-logo.8eb72ea6927b.png 500w, /cdn-cgi/image/width=1000,format=auto/https://files.realpython.com/media/python-logo.8eb72ea6927b.png 1000w" sizes="(min-width: 1000px) 207px, (min-width: 580px) 382px, calc(100vw - 134px)" alt="Python Logo" />
|
||
</div>
|
||
<div class="col">
|
||
<p id="modal-python-mastery-course-title" class="text-center h3 mb-4">
|
||
5 Thoughts On Python Mastery
|
||
</p>
|
||
<form class="col-12" action="/optins/process/" method="post">
|
||
<input type="hidden" name="csrfmiddlewaretoken" value="uS9jW4J1OA6NsjWe9e3lAb0KuP7EYOUAGX3WH46iyCnwy1uBqPKTipw66s5mt7Yj" /> <input type="hidden" name="slug" value="python-mastery-course" /> <input type="hidden" name="source_path" value="/python310-new-features/" />
|
||
<div class="form-group">
|
||
<input type="email" name="email" class="form-control" placeholder="Email Address" required="" autofocus="" />
|
||
</div><button name="submit" type="submit" class="btn btn-primary btn-block text-wrap">Start the Class »</button>
|
||
<p class="mb-0 mt-2 text-muted text-center">
|
||
<small>🔒 No spam. We take your privacy seriously.</small>
|
||
</p>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<footer class="footer">
|
||
<div class="container">
|
||
<div class="mx-auto mt-4 mb-0" style="max-width: 768px;">
|
||
<div style="display:block;position:relative;">
|
||
<div style="display:block;width:100%;padding-top:12.5%;"></div>
|
||
<div class="rpad rounded border" data-unit="8x1" style="position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;">
|
||
<a href="https://srv.realpython.net/click/30513864559/?c=62098619220&p=58946116052&r=08382" rel="nofollow" target="_blank"><img src="https://img.realpython.net/4abc783883f79f916711682eb20f448e" style="max-width: 100%; max-height: 100%; width: 100%;" /></a>
|
||
</div>
|
||
</div><a class="small text-muted" href="/account/join/" rel="nofollow"><span class="icon baseline mr-1"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#solid--circle-info"></use></svg></span>Remove ads</a>
|
||
</div>
|
||
<p class="text-center text-muted w-75 mx-auto">
|
||
© 2012–2024 Real Python ⋅ <a href="/newsletter/">Newsletter</a> ⋅ <a href="/podcasts/rpp/">Podcast</a> ⋅ <a href="https://www.youtube.com/realpython">YouTube</a> ⋅ <a href="https://twitter.com/realpython">Twitter</a> ⋅ <a href="https://facebook.com/LearnRealPython">Facebook</a> ⋅ <a href="https://www.instagram.com/realpython/">Instagram</a> ⋅ <a href="/">Python Tutorials</a> ⋅ <a href="/search">Search</a> ⋅ <a href="/privacy-policy/">Privacy Policy</a> ⋅ <a href="/energy-policy/">Energy Policy</a> ⋅ <a href="/sponsorships/">Advertise</a> ⋅ <a href="/contact/">Contact</a><br />
|
||
<span class="icon baseline text-color-red"><svg>
|
||
<use href="/static/icons.55e8f03acfe3.svg#solid--heart"></use></svg></span> Happy Pythoning!
|
||
</p>
|
||
</div>
|
||
</footer>
|
||
<script>
|
||
<![CDATA[
|
||
(function(document, history, location) {
|
||
var HISTORY_SUPPORT = !!(history && history.pushState);
|
||
|
||
var anchorScrolls = {
|
||
ANCHOR_REGEX: /^#[^ ]+$/,
|
||
OFFSET_HEIGHT_PX: 120,
|
||
|
||
/**
|
||
* Establish events, and fix initial scroll position if a hash is provided.
|
||
*/
|
||
init: function() {
|
||
this.scrollToCurrent();
|
||
window.addEventListener('hashchange', this.scrollToCurrent.bind(this));
|
||
document.body.addEventListener('click', this.delegateAnchors.bind(this));
|
||
},
|
||
|
||
/**
|
||
* Return the offset amount to deduct from the normal scroll position.
|
||
* Modify as appropriate to allow for dynamic calculations
|
||
*/
|
||
getFixedOffset: function() {
|
||
return this.OFFSET_HEIGHT_PX;
|
||
},
|
||
|
||
/**
|
||
* If the provided href is an anchor which resolves to an element on the
|
||
* page, scroll to it.
|
||
* @param {String} href
|
||
* @return {Boolean} - Was the href an anchor.
|
||
*/
|
||
scrollIfAnchor: function(href, pushToHistory) {
|
||
var match, rect, anchorOffset;
|
||
|
||
if(!this.ANCHOR_REGEX.test(href)) {
|
||
return false;
|
||
}
|
||
|
||
match = document.getElementById(href.slice(1));
|
||
|
||
if(match) {
|
||
rect = match.getBoundingClientRect();
|
||
anchorOffset = window.pageYOffset + rect.top - this.getFixedOffset();
|
||
window.scrollTo(window.pageXOffset, anchorOffset);
|
||
|
||
// Add the state to history as-per normal anchor links
|
||
if(HISTORY_SUPPORT && pushToHistory) {
|
||
history.pushState({}, document.title, location.pathname + href);
|
||
}
|
||
}
|
||
|
||
return !!match;
|
||
},
|
||
|
||
/**
|
||
* Attempt to scroll to the current location's hash.
|
||
*/
|
||
scrollToCurrent: function() {
|
||
this.scrollIfAnchor(window.location.hash);
|
||
},
|
||
|
||
/**
|
||
* If the click event's target was an anchor, fix the scroll position.
|
||
*/
|
||
delegateAnchors: function(e) {
|
||
var elem = e.target;
|
||
|
||
//
|
||
if (elem.dataset.toggle === "tab") {
|
||
return;
|
||
}
|
||
|
||
if(
|
||
elem.nodeName === 'A' &&
|
||
this.scrollIfAnchor(elem.getAttribute('href'), true)
|
||
) {
|
||
e.preventDefault();
|
||
}
|
||
}
|
||
};
|
||
|
||
window.addEventListener(
|
||
'DOMContentLoaded', anchorScrolls.init.bind(anchorScrolls)
|
||
);
|
||
})(window.document, window.history, window.location);
|
||
]]>
|
||
</script>
|
||
<script>
|
||
<![CDATA[
|
||
|
||
(function() {
|
||
var isAndroid = navigator.userAgent.toLowerCase().indexOf("android") > -1;
|
||
if (!isAndroid) {
|
||
return;
|
||
}
|
||
|
||
var styles = `
|
||
@font-face {
|
||
font-family: 'DejaVu Sans Mono';
|
||
font-weight: normal;
|
||
font-style: normal;
|
||
font-display: swap;
|
||
src: url('/static/mfonts/dejavu-sans-mono.33f00225f915.woff2') format('woff2'),
|
||
url('/static/mfonts/dejavu-sans-mono.0da77d3739f3.woff') format('woff'),
|
||
url('/static/mfonts/dejavu-sans-mono.c2356fc49835.ttf') format('truetype');
|
||
}
|
||
code, kbd, pre, samp {
|
||
font-family: 'DejaVu Sans Mono', monospace;
|
||
}
|
||
`
|
||
|
||
var styleSheet = document.createElement("style")
|
||
styleSheet.type = "text/css"
|
||
styleSheet.innerText = styles
|
||
document.head.appendChild(styleSheet)
|
||
})();
|
||
]]>
|
||
</script>
|
||
<script src="/static/jquery.min.00727d1d5d9c.js"></script>
|
||
<script src="/static/popper.min.47dc3aaf2942.js"></script>
|
||
<script src="/static/bootstrap.min.a3b2689424c3.js"></script>
|
||
<script>
|
||
<![CDATA[
|
||
|
||
(function() {
|
||
document.querySelectorAll(".js-search-form-submit").forEach(function(el) {
|
||
el.addEventListener("click", function(e) {
|
||
e.preventDefault();
|
||
e.currentTarget.parentElement.submit();
|
||
})
|
||
});
|
||
})();
|
||
]]>
|
||
</script>
|
||
<script src="/static/frontend/reader/codeblock.5c8399b698d9.js" async="async"></script>
|
||
<script src="/static/frontend/reader/lightbox.45ed8c5a3d27.js" async="async"></script>
|
||
<script src="/static/frontend/reader/platforms-ui.b11202dc6079.js" async="async"></script>
|
||
<script>
|
||
<![CDATA[
|
||
window.rp_prop_id = '58946116052';
|
||
]]>
|
||
</script>
|
||
<script src="https://srv.realpython.net/tag.js" async="async"></script>
|
||
<script src="/static/frontend/reader/toc-refresh.f019e7905d90.js" async="async"></script>
|
||
<script id="js-context" type="application/json">
|
||
<![CDATA[
|
||
{"is_completed": false, "is_bookmarked": false, "api_article_bookmark_url": "/api/v1/articles/python310-new-features/bookmark/", "api_article_completion_status_url": "/api/v1/articles/python310-new-features/completion_status/"}
|
||
]]>
|
||
</script>
|
||
<script src="/static/frontend/reader/completion-status.dd9ca59c605f.js" async="async"></script>
|
||
<script id="dsq-count-scr" src="https://realpython.disqus.com/count.js" async="async"></script>
|
||
<script>
|
||
<![CDATA[
|
||
|
||
var disqus_config = function () {
|
||
this.page.url = 'https://realpython.com/python310-new-features/';
|
||
this.page.identifier = 'https://realpython.com/python310-new-features/';
|
||
this.callbacks.onReady = [function() {
|
||
if (window.onDisqusReady) {
|
||
window.onDisqusReady();
|
||
}
|
||
}];
|
||
};
|
||
var disqus_script_url = 'https://realpython.disqus.com/embed.js';
|
||
]]>
|
||
</script>
|
||
<script src="/static/frontend/reader/lazy-disqus.07ee9079f4a3.js" defer="defer"></script>
|
||
<script src="https://cdn.onesignal.com/sdks/OneSignalSDK.js" async="async"></script>
|
||
<script>
|
||
<![CDATA[
|
||
|
||
var OneSignal = window.OneSignal || [];
|
||
OneSignal.push(function() {
|
||
OneSignal.init({
|
||
appId: "c0081e20-a523-42bb-b0ac-04c5a9e8bf40"
|
||
});
|
||
});
|
||
]]>
|
||
</script>
|
||
<script type="application/ld+json">
|
||
<![CDATA[
|
||
|
||
{
|
||
"@context": "http://schema.org",
|
||
"@type": "Article",
|
||
"headline": "Python 3.10: Cool New Features for You to Try",
|
||
|
||
"image": {
|
||
"@type": "ImageObject",
|
||
"url": "https://files.realpython.com/media/Python-3.10-Cool-New-Features-for-You-to-Try_Watermarked.e2782d8a16dc.jpg",
|
||
"width": 1920,
|
||
"height": 1080
|
||
},
|
||
|
||
"mainEntityOfPage": {
|
||
"@type": "WebPage",
|
||
"@id": "https://realpython.com/python310-new-features/"
|
||
},
|
||
"datePublished": "2021-10-04T14:00:00+00:00",
|
||
"dateModified": "2023-07-31T20:55:51.232741+00:00",
|
||
"publisher": {
|
||
"@type": "Organization",
|
||
"name": "Real Python",
|
||
"logo": {
|
||
"@type": "ImageObject",
|
||
"url": "/static/real-python-logo-square-tiny.b2452b6d3823.png",
|
||
"width": 60,
|
||
"height": 60
|
||
}
|
||
},
|
||
"author": {
|
||
"@type": "Organization",
|
||
"name": "Real Python",
|
||
"url": "https://realpython.com",
|
||
"logo": "/static/real-python-logo-square.146e987bf77c.png"
|
||
},
|
||
"description": "In this tutorial, you\u0027ll explore some of the coolest and most useful features in Python 3.10. You\u0027ll appreciate more user\u002Dfriendly error messages, learn about how you can handle complicated data structures with structural pattern matching, and explore new enhancements to Python\u0027s type system."
|
||
}
|
||
]]>
|
||
</script>
|
||
<script src="/static/frontend/optins/optins.2cd33855b6a0.js" async="async"></script>
|
||
<script src="/static/frontend/search/autocomplete.5cf5140902a7.js" async="async"></script>
|
||
<script>
|
||
<![CDATA[
|
||
|
||
!function(f,b,e,v,n,t,s)
|
||
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
|
||
n.callMethod.apply(n,arguments):n.queue.push(arguments)};
|
||
if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
|
||
n.queue=[];t=b.createElement(e);t.async=!0;
|
||
t.src=v;s=b.getElementsByTagName(e)[0];
|
||
s.parentNode.insertBefore(t,s)}(window, document,'script',
|
||
'https://connect.facebook.net/en_US/fbevents.js');
|
||
fbq('init', '2220911568135371');
|
||
fbq('track', 'PageView');
|
||
]]>
|
||
</script> <noscript><img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=2220911568135371&ev=PageView&noscript=1" /></noscript>
|
||
<script>
|
||
<![CDATA[
|
||
(function(){var js = "window['__CF$cv$params']={r:'84a8bd358b8c4097',t:'MTcwNjEwNDE2MC40NDYwMDA='};_cpo=document.createElement('script');_cpo.nonce='',_cpo.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js',document.getElementsByTagName('head')[0].appendChild(_cpo);";var _0xh = document.createElement('iframe');_0xh.height = 1;_0xh.width = 1;_0xh.style.position = 'absolute';_0xh.style.top = 0;_0xh.style.left = 0;_0xh.style.border = 'none';_0xh.style.visibility = 'hidden';document.body.appendChild(_0xh);function handler() {var _0xi = _0xh.contentDocument || _0xh.contentWindow.document;if (_0xi) {var _0xj = _0xi.createElement('script');_0xj.innerHTML = js;_0xi.getElementsByTagName('head')[0].appendChild(_0xj);}}if (document.readyState !== 'loading') {handler();} else if (window.addEventListener) {document.addEventListener('DOMContentLoaded', handler);} else {var prev = document.onreadystatechange || function () {};document.onreadystatechange = function (e) {prev(e);if (document.readyState !== 'loading') {document.onreadystatechange = prev;handler();}};}})();
|
||
]]>
|
||
</script><iframe height="1" width="1" style="position: absolute; top: 0px; left: 0px; border: none; visibility: hidden;"></iframe>
|
||
<div id="onesignal-bell-container" class="onesignal-bell-container onesignal-reset onesignal-bell-container-bottom-left">
|
||
<div id="onesignal-bell-launcher" class="onesignal-bell-launcher onesignal-bell-launcher-md onesignal-bell-launcher-bottom-left onesignal-bell-launcher-theme-default onesignal-bell-launcher-active" style="bottom: 15px; left: 15px;">
|
||
<div class="onesignal-bell-launcher-button">
|
||
<svg class="onesignal-bell-svg" xmlns="http://www.w3.org/2000/svg" width="99.7" height="99.7" viewbox="0 0 99.7 99.7" style="filter: drop-shadow(0 2px 4px rgba(34,36,38,0.35));; -webkit-filter: drop-shadow(0 2px 4px rgba(34,36,38,0.35));;">
|
||
<circle class="background" cx="49.9" cy="49.9" r="49.9" style="fill: rgb(208, 2, 27);"></circle>
|
||
<path class="foreground" d="M50.1 66.2H27.7s-2-.2-2-2.1c0-1.9 1.7-2 1.7-2s6.7-3.2 6.7-5.5S33 52.7 33 43.3s6-16.6 13.2-16.6c0 0 1-2.4 3.9-2.4 2.8 0 3.8 2.4 3.8 2.4 7.2 0 13.2 7.2 13.2 16.6s-1 11-1 13.3c0 2.3 6.7 5.5 6.7 5.5s1.7.1 1.7 2c0 1.8-2.1 2.1-2.1 2.1H50.1zm-7.2 2.3h14.5s-1 6.3-7.2 6.3-7.3-6.3-7.3-6.3z" style="fill: rgb(255, 255, 255);"></path>
|
||
<ellipse class="stroke" cx="49.9" cy="49.9" rx="37.4" ry="36.9" style="stroke: rgb(255, 255, 255);"></ellipse></svg>
|
||
</div>
|
||
<div class="onesignal-bell-launcher-badge" style="filter: drop-shadow(0 2px 4px rgba(34,36,38,0));; -webkit-filter: drop-shadow(0 2px 4px rgba(34,36,38,0));;"></div>
|
||
<div class="onesignal-bell-launcher-message">
|
||
<div class="onesignal-bell-launcher-message-body"></div>
|
||
</div>
|
||
<div class="onesignal-bell-launcher-dialog" style="filter: drop-shadow(0px 2px 2px rgba(34,36,38,.15));; -webkit-filter: drop-shadow(0px 2px 2px rgba(34,36,38,.15));;">
|
||
<div class="onesignal-bell-launcher-dialog-body"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</body>
|
||
</html>
|