drogon/tests/parsing_utils_test.py

2755 lines
377 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import pytest
from bs4 import BeautifulSoup
from core.parsing_utils import (
ParsingFlow,
ParsingFlowGenerator,
ReferenciaFieldInstructions,
PrecioFieldInstructions,
TamanoCategoricoFieldInstructions,
M2FieldInstructions,
TipoAnuncioFieldInstructions,
CalleFieldInstructions,
BarrioFieldInstructions,
DistritoFieldInstructions,
CiudadFieldInstructions,
SecondaryFeaturesFieldInstructions,
TelefonoFieldInstructions,
)
@pytest.fixture
def real_ad_html():
html = """
<!DOCTYPE html>
<html lang="es" env="es" username="" data-userAuth="false"> <head> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <title>Garaje en venta en Passatge de Simó, 10, La Sagrada Família, Barcelona &#8212; idealista</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta name="description" content="garaje de 9 m², Garaje en venta en Passatge de Simó, 10, La Sagrada Família, Barcelona, barrio La Sagrada Família"> <meta name="author" content="idealista.com"> <meta http-equiv="cleartype" content="on"> <meta name="pragma" content="no-cache"/> <meta http-equiv="Pragma" content="no-cache"/> <meta http-equiv="Expires" content="Fri, 01 Jan 1990 00:00:00 GMT"/> <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate"/> <script src="https://st1.idealista.com/static/common/release/mavericks/legacy/js/didomi/didomi-consent-lang.js"></script> <script src="https://st1.idealista.com/static/common/release/mavericks/legacy/js/didomi/didomi-iab-integration.js"></script> <script src="https://st1.idealista.com/static/common/release/mavericks/legacy/js/didomi/didomi-embed.js"></script> <script src="https://st1.idealista.com/static/common/release/mavericks/legacy/js/tealium/tealium-wrapper.js?20201222-0907"></script> <link rel="shortcut icon" href="https://st1.idealista.com/favicon.ico?20201222-0907"> <link rel="apple-touch-icon" sizes="32x32" href="https://st1.idealista.com/static/common/icons/32x32.png?20201222-0907"> <link rel="apple-touch-icon-precomposed" href="https://st1.idealista.com/static/common/icons/32x32.png?20201222-0907"> <link rel="apple-touch-icon" sizes="57x57" href="https://st1.idealista.com/static/common/icons/57x57.png?20201222-0907"> <link rel="apple-touch-icon-precomposed" href="https://st1.idealista.com/static/common/icons/57x57.png?20201222-0907"> <link rel="apple-touch-icon" href="https://st1.idealista.com/static/common/icons/60x60.png?20201222-0907"> <link rel="apple-touch-icon-precomposed" href="https://st1.idealista.com/static/common/icons/60x60.png?20201222-0907"> <link rel="apple-touch-icon" sizes="76x76" href="https://st1.idealista.com/static/common/icons/76x76.png?20201222-0907"> <link rel="apple-touch-icon-precomposed" sizes="76x76" href="https://st1.idealista.com/static/common/icons/76x76.png?20201222-0907"> <link rel="apple-touch-icon" sizes="96x96" href="https://st1.idealista.com/static/common/icons/96x96.png?20201222-0907"> <link rel="apple-touch-icon-precomposed" href="https://st1.idealista.com/static/common/icons/96x96.png?20201222-0907"> <link rel="apple-touch-icon" sizes="120x120" href="https://st1.idealista.com/static/common/icons/120x120.png?20201222-0907"> <link rel="apple-touch-icon-precomposed" sizes="120x120" href="https://st1.idealista.com/static/common/icons/120x120.png?20201222-0907"> <link rel="apple-touch-icon" sizes="128x128" href="https://st1.idealista.com/static/common/icons/128x128.png?20201222-0907"> <link rel="apple-touch-icon-precomposed" href="https://st1.idealista.com/static/common/icons/128x128.png?20201222-0907"> <link rel="apple-touch-icon" sizes="144x144" href="https://st1.idealista.com/static/common/icons/144x144.png?20201222-0907"> <link rel="apple-touch-icon-precomposed" href="https://st1.idealista.com/static/common/icons/144x144.png?20201222-0907"> <link rel="apple-touch-icon" sizes="152x152" href="https://st1.idealista.com/static/common/icons/152x152.png?20201222-0907"> <link rel="apple-touch-icon-precomposed" sizes="152x152" href="https://st1.idealista.com/static/common/icons/152x152.png?20201222-0907"> <link rel="apple-touch-icon" sizes="167x167" href="https://st1.idealista.com/static/common/icons/167x167.png?20201222-0907"> <link rel="apple-touch-icon-precomposed" href="https://st1.idealista.com/static/common/icons/167x167.png?20201222-0907"> <link rel="apple-touch-icon" sizes="180x180" href="https://st1.idealista.com/static/common/icons/180x180.png?20201222-0907"> <link rel="apple-touch-icon-precomposed" href="https://st1.idealista.com/static/common/icons/180x180.png?20201222-0907"> <link rel="apple-touch-icon" sizes="192x192" href="https://st1.idealista.com/static/common/icons/192x192.png?20201222-0907"> <link rel="apple-touch-icon-precomposed" href="https://st1.idealista.com/static/common/icons/192x192.png?20201222-0907"> <link rel="apple-touch-icon" sizes="195x195" href="https://st1.idealista.com/static/common/icons/195x195.png?20201222-0907"> <link rel="apple-touch-icon-precomposed" href="https://st1.idealista.com/static/common/icons/195x195.png?20201222-0907"> <link rel="apple-touch-icon" sizes="196x196" href="https://st1.idealista.com/static/common/icons/196x196.png?20201222-0907"> <link rel="apple-touch-icon-precomposed" href="https://st1.idealista.com/static/common/icons/196x196.png?20201222-0907"> <meta name="apple-itunes-app" content="app-id=465958311"/> <meta name="google-play-app" content="app-id=com.idealista.android"/> <meta name="app-download-url" content="/download"/> <script type="didomi/javascript" data-vendor="c:abtasty-LLkECCj8" async="" charset="utf-8" src="//try.abtasty.com/ba99abce363c777e429f2eef6f87c927.js"></script> <link rel="stylesheet" href="https://st1.idealista.com/static/common/release/detail/detail.css?20201222-0907" type="text/css"/> <link rel="canonical" href="https://www.idealista.com/inmueble/92174198/"/> <meta name="ROBOTS" content="NOODP,INDEX, FOLLOW"/> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=2.0, minimum-scale=1.0"> <meta property="og:image" content="https://img3.idealista.com/blur/WEB_DETAIL-M-L/0/id.pro.es.image.master/23/bc/87/832773863.jpg"> <meta property="og:image:width" content="1500"> <meta property="og:image:height" content="1125"> <script src='//ec-ns.sascdn.com/diff/js/smart.js' type="didomi/javascript" data-vendor= "iab:45" async></script> <script type="didomi/javascript" data-vendor= "iab:45">
var sas = sas || {};
sas.cmd = sas.cmd || [];
sas.cmd.push(function() {
if(sas.setup) {
sas.setup({domain: '//smart.idealista.com', async: true, renderMode: 2});
}
});
</script> <script type="didomi/javascript" data-vendor= "iab:45" id="advertisementScript">
var sas = sas || {};
sas.cmd = sas.cmd || [];
sas.cmd.push(function() {
if(sas.call) {
sas.call('onecall', {
siteId: 33114,
pageId: 253244,
formatId: '16025,16023',
target: 'geo=0-EU-ES-08-13-001-019-02-005-XX-XX;typ=gar;geop=0-EU-ES-08;ope=s;geoc=0-EU-ES-08-13;auth=f'
}, {
onNoad: function(data){
if (data.formatId) {
var bannerDiv = document.getElementById('banners_' + data.formatId);
if (bannerDiv != null) {
bannerDiv.style.display = 'none';
}
}
}
});
}
});
</script> <link rel="alternate" hreflang="ca" href="https://www.idealista.com/ca/inmueble/92174198/"/><link rel="alternate" hreflang="en" href="https://www.idealista.com/en/inmueble/92174198/"/><link rel="alternate" hreflang="fr" href="https://www.idealista.com/fr/inmueble/92174198/"/><link rel="alternate" hreflang="de" href="https://www.idealista.com/de/inmueble/92174198/"/><link rel="alternate" hreflang="it" href="https://www.idealista.com/it/inmueble/92174198/"/><link rel="alternate" hreflang="pt" href="https://www.idealista.com/pt/inmueble/92174198/"/><link rel="alternate" hreflang="da" href="https://www.idealista.com/da/inmueble/92174198/"/><link rel="alternate" hreflang="fi" href="https://www.idealista.com/fi/inmueble/92174198/"/><link rel="alternate" hreflang="no" href="https://www.idealista.com/no/inmueble/92174198/"/><link rel="alternate" hreflang="nl" href="https://www.idealista.com/nl/inmueble/92174198/"/><link rel="alternate" hreflang="pl" href="https://www.idealista.com/pl/inmueble/92174198/"/><link rel="alternate" hreflang="ro" href="https://www.idealista.com/ro/inmueble/92174198/"/><link rel="alternate" hreflang="ru" href="https://www.idealista.com/ru/inmueble/92174198/"/><link rel="alternate" hreflang="sv" href="https://www.idealista.com/sv/inmueble/92174198/"/><link rel="alternate" hreflang="el" href="https://www.idealista.com/el/inmueble/92174198/"/><link rel="alternate" hreflang="zh" href="https://www.idealista.com/zh/inmueble/92174198/"/> </head> <body id=""> <script type="didomi/javascript" data-vendor="c:at-internet">
<!--
xtnv = document;
xtsd = "https://logs3";
xtsite = "352991";
xtdmc = ".idealista.com";
xtn2 = "7";
xtpage = "detalle::home";
xtdi = "0";
xt_multc = "";
xt_orderid = "";
xt_roimt = "";
xt_an = "anonymous";
xt_ac = "2";
clic = "#clic#";
xt_tags = "[sin_tag]";
if (window.xtparam != null) {
window.xtparam += "&tag=" + xt_tags;
} else {
window.xtparam = "&tag=" + xt_tags;
};
if (window.xtparam!=null) {
window.xtparam+="&cmd="+xt_orderid+"&an="+xt_an+"&ac="+xt_ac+"&roimt="+xt_roimt+xt_multc;
} else {
window.xtparam="&cmd="+xt_orderid+"&an="+xt_an+"&ac="+xt_ac+"&roimt="+xt_roimt+xt_multc;
};
//-->
</script><script type='didomi/javascript' data-vendor='c:at-internet' src='https://st1.idealista.com/static/common/js/ext/xiti/xiti.js' async></script> <script type="text/javascript" src="https://st1.idealista.com/static/common/js/criteo/ld.js?20201222-0907" async="true"></script> <script type="text/javascript">
window.criteo_q = window.criteo_q || [];
window.criteo_q.push(
{ event: "setAccount", account: [13122, 19089, 31531] },
{ event: "setCustomerId", id: "" },
{ event: "setSiteType", type: "d" },
{ event: "viewItem", item: "92174198" }
);
</script> <script type="text/javascript">
var utag_data = {"page":{"section":"portal","subSection":"detail","name":"home","language":"es","category":"detail","subCategory":"","subSubCategory":"","template":""},"user":{"status":"2"},"response":{"statusCode":"200","loadBalancer":"web"},"ad":{"id":"92174198","operation":"1","typology":"4","address":{"municipalityId":"0-EU-ES-08-13-001-019","provinceId":"0-EU-ES-08","locationId":"0-EU-ES-08-13-001-019-02-005","locationLevel":"8"},"price":"17900.0","characteristics":{"hasLift":"0","constructedArea":"9.15"},"condition":{},"media":{"photoNumber":"4","videoNumber":"0","hasFloorPlan":"0","has3Dtour":"0","hasHomeStaging":"0"},"owner":{"type":"1","commercialId":"","contactPreference":"1"},"origin":"1","isRecommended":"0","hasRecommended":"0","numberRecommended":"","originTypeRecommended":""},"search":{"operation":"1","typology":"4","geo":{"type":"OFFICIAL_ZONE","locationId":"0-EU-ES-08-13-001-019-02-005","locationLevel":"8"},"orderBy":"relevance-desc"},"correlation":{"id":"2515b437-2656-43e1-8709-86d80b5e9803","session":"e90e4a02-0586-4791-9f8c-100926262753"},"mustExclude":"1","didomiVendorsConsent":""};
var dataLayerContext = utag_data;
var toggleTealiumClicks = true
window.utag_cfg_ovrd = { noview: true };
window.utag_data = utag_data;
</script> <script type="text/javascript">
(function(a,b,c,d){
a='//tags.tiqcdn.com/utag/idealista/es-portal/prod/utag.js';
b=document;c='script';d=b.createElement(c);d.onload=function () { window.idealistaUtag && window.idealistaUtag.init() };d.src=a;d.type='text/java'+c;d.async=true;
a=b.getElementsByTagName(c)[0];a.parentNode.insertBefore(d,a);
})();
</script> <script type="text/javascript">
var detailWithSuggestionsToggle = true
if(!detailWithSuggestionsToggle || !false) {
function onInitIdealistaUtag() {
window.utag.view(window.utag_data);
}
if (window.idealistaUtag) {
window.idealistaUtag.onInit(onInitIdealistaUtag);
}
}
</script> <header id="main-header" class=" help-center ide-header "> <div class="col-ide-lg-12 col-ide-xl-12 content"> <figure class="logo-container "> <a href='/' class='id-logo'> <img alt='idealista' src='https://st1.idealista.com/static/common/img/idealista.svg' title='idealista'></img></a> </figure> <nav> <div class="lang-selector lang-responsive"> <span class="lang-selector--lang-selected icon-arrow-dropdown"> <span class="flag lang-es"></span> <span class="lang-text">Español</span> </span> <ul class="lang-selector--lang-options" data-url-attribute="/user/update-user-language" data-user-authenticated="false"> <li> <a rel="nofollow" id="idioma-ca" value="ca" href="/ca/inmueble/92174198/" data-markup="cambio::idioma::cabecera::ca"> <span> <span class="flag lang-ca"></span>Català </span> </a> </li> <li> <a rel="nofollow" id="idioma-en" value="en" href="/en/inmueble/92174198/" data-markup="cambio::idioma::cabecera::en"> <span> <span class="flag lang-en"></span>English </span> </a> </li> <li> <a rel="nofollow" id="idioma-fr" value="fr" href="/fr/inmueble/92174198/" data-markup="cambio::idioma::cabecera::fr"> <span> <span class="flag lang-fr"></span>Français </span> </a> </li> <li> <a rel="nofollow" id="idioma-de" value="de" href="/de/inmueble/92174198/" data-markup="cambio::idioma::cabecera::de"> <span> <span class="flag lang-de"></span>Deutsch </span> </a> </li> <li> <a rel="nofollow" id="idioma-it" value="it" href="/it/inmueble/92174198/" data-markup="cambio::idioma::cabecera::it"> <span> <span class="flag lang-it"></span>Italiano </span> </a> </li> <li> <a rel="nofollow" id="idioma-pt" value="pt" href="/pt/inmueble/92174198/" data-markup="cambio::idioma::cabecera::pt"> <span> <span class="flag lang-pt"></span>Português </span> </a> </li> <li> <a rel="nofollow" id="idioma-da" value="da" href="/da/inmueble/92174198/" data-markup="cambio::idioma::cabecera::da"> <span> <span class="flag lang-da"></span>Dansk </span> </a> </li> <li> <a rel="nofollow" id="idioma-fi" value="fi" href="/fi/inmueble/92174198/" data-markup="cambio::idioma::cabecera::fi"> <span> <span class="flag lang-fi"></span>Suomi </span> </a> </li> <li> <a rel="nofollow" id="idioma-no" value="no" href="/no/inmueble/92174198/" data-markup="cambio::idioma::cabecera::no"> <span> <span class="flag lang-no"></span>Norsk </span> </a> </li> <li> <a rel="nofollow" id="idioma-nl" value="nl" href="/nl/inmueble/92174198/" data-markup="cambio::idioma::cabecera::nl"> <span> <span class="flag lang-nl"></span>Nederlands </span> </a> </li> <li> <a rel="nofollow" id="idioma-pl" value="pl" href="/pl/inmueble/92174198/" data-markup="cambio::idioma::cabecera::pl"> <span> <span class="flag lang-pl"></span>Polski </span> </a> </li> <li> <a rel="nofollow" id="idioma-ro" value="ro" href="/ro/inmueble/92174198/" data-markup="cambio::idioma::cabecera::ro"> <span> <span class="flag lang-ro"></span>Română </span> </a> </li> <li> <a rel="nofollow" id="idioma-ru" value="ru" href="/ru/inmueble/92174198/" data-markup="cambio::idioma::cabecera::ru"> <span> <span class="flag lang-ru"></span>Русский </span> </a> </li> <li> <a rel="nofollow" id="idioma-sv" value="sv" href="/sv/inmueble/92174198/" data-markup="cambio::idioma::cabecera::sv"> <span> <span class="flag lang-sv"></span>Svenska </span> </a> </li> <li> <a rel="nofollow" id="idioma-el" value="el" href="/el/inmueble/92174198/" data-markup="cambio::idioma::cabecera::el"> <span> <span class="flag lang-el"></span>Ελληνικά </span> </a> </li> <li> <a rel="nofollow" id="idioma-zh" value="zh" href="/zh/inmueble/92174198/" data-markup="cambio::idioma::cabecera::zh"> <span> <span class="flag lang-zh"></span>中文 </span> </a> </li> <li class="separator"> <a href="https://www.idealista.it/es/" title="" data-markup="cambio::pais::cabecera::it">idealista.it</a> </li> <li> <a href="https://www.idealista.pt/es/" title="" data-markup="cambio::pais::cabecera::pt">idealista.pt</a> </li> </ul> </div> <ul class="no-logged-user-menu ide-main-menu" id="no-login-user-bar"> <li id="put-adv-free" class="ide-main-menu__adv-free"> <a data-markup="header-new-advert" href="/info/publicar-anuncio"> <span class="icon-pin"> Pon tu anuncio gratis </span> </a> </li> <li id="your-account"> <a data-markup="header-login" href="/login" rel="nofollow" class="icon-user-no-logged"> Acceso<span class="hide-txt"> usuarios</span> </a> </li> </ul> </nav> </div> </header> <div id="wrapper"> <div id="main" data-isLogged="false"> <div class="container"> <div id="pager" class="detail-pagination"> <div class="content detail-first-picture"> <nav class="detail-pagination--back"> <a class="btn nav back icon-arrow-double-left" href="/venta-garajes/barcelona/eixample/la-sagrada-familia/" title="Garajes en La Sagrada Família"> Garajes en La Sagrada Família </a> </nav> <p class="detail-pagination--count"> </p> <nav class="detail-pagination--prev-next"> </nav> </div> </div> <main class="detail-container"> <section class="main-image"></section> <aside id="side-content" class="side-content"> <section class="module-contact highlight-content" contact-modal-type="detail"> <input type="hidden" name="typology" value="4"> <div class="module-contact--border"> <div class="feedback-container-unblock"></div> <h3 class="module-contact_title h3-simulated"> <strong>Pregunta al anunciante</strong> </h3> <div class="advertiser-info"> <div class="advertiser-info__text advertiser-info__gallery"> <p class="advertiser-name"> María </p> <div class="shortAdDescription"> <p class="ellipsis">Garaje en venta en Passatge de Simó, 10</p> </div> </div> </div> <div class="ide-box-contact feedback-save-search feedback-container d-none"></div> <div class="module-contact_form"> <form action="" class="form contact" data-service-error='¡Vaya! No hemos podido enviar tu mensaje ¡Prueba otra vez!' novalidate> <div class="module-contact_feedback "> <div class="feedback success icon-sent-ok"> <span class="feedback_message"> Has contactado </span> <span class="contacted-time"> </span> </div> </div> <div class="advertiser-comment item-form no-margin"> <label> <p class="module-contact_contacted-title ">Tu mensaje fue</p> <textarea class="module-contact_textarea" name="contact-message" data-validation="required" data-message-required="Muestra tu interés escribiendo un mensaje" placeholder="¿Alguna duda? ¿Quieres visitar el inmueble? ¡Pregunta al anunciante!">Hola, me interesa este garaje y me gustaría hacer una visita.
Un saludo</textarea> <ul class="last-contact-messages"></ul> </label> </div> <div class="collapsed-form"> <div class="item-form"> <div> <label> <input type="hidden" name="email2"/> <span>Tu email</span> <input name="contact-email" type="email" class="js-email-field" value="" autocomplete="email" data-validation="email" data-message-email='Revisa el formato de tu email' data-required-group="contact-data" data-contact-data-error='Indica un email o teléfono para que te puedan contestar ;-)'> </label> </div> </div> <div class="item-form half-items clearfix"> <div> <label> <span>Tu teléfono</span> <input name="contact-phone" type="tel" class="js-phone-field" value="" autocomplete="tel" data-validation="phone" data-message-phone='Revisa tu teléfono' data-required-group="contact-data"> </label> </div> <div> <label> <span>Tu nombre</span> <input name="contact-name" type="text" autocomplete="name" data-validation="name" value="" data-message-required="Indica tu nombre " data-message-name="Esto no parece un nombre"> </label> </div> </div> <div class="general-errors-container feedback full-width warning icon-feedbk-alert d-none"></div> <div class="service-error-container feedback full-width error icon-feedbk-ko d-none"></div> <div class="captcha-miniFields d-none" data-service-prefix=""> <div class="captcha"> <div class="captcha-img"> </div> </div> <div class="item-form half-items clearfix"> <label> <span>Escribe los números y letras que ves arriba</span> <div> <input type="text" class="_captcha" name="contact-captcha" autocapitalize="none" autocorrect="off"/> </div> </label> <p><a class="captcha-reload" href="#">¿No entiendes lo que pone?</a></p> </div> </div> <div id="privacy-policy-detail" class="item-form privacy-policy-container"> <div> <label class="input-checkbox" data-validation="checkbox" data-message-checkbox="Para contactar debes aceptar la política de privacidad" data-error-name="privacy-policy-checkbox"> <input type="checkbox" name="privacy-policy-checkbox"/> <span> <span> Aceptar <a href="https://www.idealista.com/ayuda/articulos/politica-de-privacidad/" target="_blank">política de privacidad</a> </span> </span> </label> </div> </div> <div id="recommendations-detail" class="item-form recommendations-container"> <div> <label class="input-checkbox" data-error-name="recommendations-checkbox"> <input type="checkbox" name="recommendations-checkbox" data-markup-recommendations="detalle::conversiones::form_contacto::recomendations-"/> <span> <span> Recibir inmuebles de idealista similares a este </span> </span> </label> </div> </div> <input type="hidden" name="adId" value="92174198"/> <input type="hidden" name="operationId" value="1"/> <input type="hidden" name="type" id="contact-type" value="1"/> </div> <div class="d-none"> <input type="text" value="" name="contact-mail"/> </div> <div> <input type="submit" class="btn action txt-bold txt-big desktop" value="Contactar" data-contacted="Contactar de nuevo"/> <input type="submit" class="btn action txt-bold txt-big no-desktop" value="Enviar" data-contacted="Enviar de nuevo"/> </div> </form> </div> <input type="hidden" name="adId" value="92174198"/> <div class="ide-box-contact advertiser-data"> <div class="contact-phones"> <div> <div class="phone last-phone"> <p class="txt-bold _browserPhone icon-phone"> 660 611 218 </p> </div> </div> </div> <p class="txt-ref"> Anuncio: 92174198 </p> </div> <div class="ide-box-contact module-contact-gray"> <h2 class="txt-big txt-bold mb-small"> Anunciante </h2> <div class="professional-name"> <div class="name"> Particular </div> <span class="particular"> <span class="icon-block"></span> María <input type="hidden" name="user-name" value="María "/> </span> </div> </div> </section> <nav id="sticky-contact" class="sticky-contact-detail"> <div class="sticky-contact-user col-ide-xs-6 col-ide-sm-4 two-buttons"> <div class="sticky-contact-user-info"> <div class="name"> Particular </div> <span class="particular"> <p class="about-advertiser-name"> María </p> </span> </div> </div> <div class="contact-buttons col-ide-xs-12 col-ide-sm-8 two-buttons"> <a class="_mobilePhone phone-number icon-phone btn action col-ide-xs-6 col-ide-sm-6" target="_blank" href="tel:660611218"> <span class="phone-btn-number">660 611 218</span> <span class="phone-btn-txt">Llamar</span> </a> <button class="contact-fake email-btn icon-chat btn action col-ide-xs-6 col-ide-sm-6" data-role=""> <span>Contactar</span> </button> </div> </nav> </aside> <section class="detail-info ide-box-detail-first-picture ide-box-detail--reset overlay-box"> <nav id="fixed-toolbar" class="sticky-bar-detail"> <div class="content clearfix col-ide-lg-12 col-ide-xl-12"> <a class="nav back icon-arrow-double-left" href="/venta-garajes/barcelona/eixample/la-sagrada-familia/" title="Garajes en La Sagrada Família"></a> <div class="main-info"> <h2 class="txt-body "> <span>Garaje en venta en Passatge de Simó, 10</span> </h2> <strong class="typology"> garaje </strong> <p class="info-data txt-big"> <strong class="price">17.900 €</strong> <span> Plaza para <span>coche pequeño</span> </span> <span> <span>9,15 m²</span> </span> </p> </div> <div class="links"> <button class="favorite-btn btn regular" data-role="add" data-text-add='Guardar<span class="hide-txt"> favorito</span>' data-text-remove='Favorito' data-markup="favourite-toolbar" data-markup-add="favourite-toolbar" data-markup-remove="remove-favourite-toolbar"/> <svg class="icon-heart" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <title>Guardar<span class="hide-txt"> favorito</span></title> <g id="svg-heart" stroke-width="1" fill-rule="evenodd"> <g id="Group" transform="translate(1.469388, 1.959184)"> <path d="M10.5494545,4.34654416 C13.0317121,-2.66965835 21.0989011,-0.423454109 21.0989011,5.63737113 C21.0989011,12.2223851 10.5494545,20.5714286 10.5494545,20.5714286 C10.5494545,20.5714286 0,12.2223851 0,5.63737113 C0,-0.423454109 8.0671916,-2.66965835 10.5494545,4.34654416 Z" id="fav-svg-path"></path> </g> </g> <g id="svg-heart-fill" stroke-width="1" fill-rule="evenodd"> <g id="Group" transform="translate(1.469388, 1.959184)"> <path d="M10.5494545,4.34654416 C13.0317121,-2.66965835 21.0989011,-0.423454109 21.0989011,5.63737113 C21.0989011,12.2223851 10.5494545,20.5714286 10.5494545,20.5714286 C10.5494545,20.5714286 0,12.2223851 0,5.63737113 C0,-0.423454109 8.0671916,-2.66965835 10.5494545,4.34654416 Z" id="fav-svg-fill"></path> </g> </g> </svg> <span>Guardar<span class="hide-txt"> favorito</span></span> </button> <button class="discard-btn icon-delete" data-role="add" data-text-add='Descartar' data-text-remove='Recuperar' data-markup="discard-toolbar" data-markup-add="discard-toolbar" data-markup-remove="recover-toolbar"/> <span>Descartar</span> </button> </div> </div> </nav> <div class="main-info__title"> <h1 class="h2-simulated "> <span class="main-info__title-main">Garaje en venta en Passatge de Simó, 10</span> </h1> <span class="main-info__title-block"> <span class="main-info__title-minor">La Sagrada Família, Barcelona</span> <a href="#" class="main-info__title-map icon-location showMap" data-markup-map='detalle::map::top'><span>Ver mapa</span></a> </span> </div> <div class="info-data"> <span class="info-data-price"><span class="txt-bold">17.900</span> €</span> </div> <div class="info-tags"> <span class="txt-mortgage"> <span> <a href="/hipotecas/hipoteca-para-comprar-casa?adId=92174198&xtatc=INT-8-[detalle_personalizar_hipoteca_consecutivo]" class="icon-barchart" data-markup="calculate-mortgage-top" target="_blank"> <span>Calcular hipoteca</span> </a> </span> </span> <span class="txt-mortgage"> <span class="applyMortgageContainer "> <a href="#" data-markup="form-mortgages-top" class="icon-edit applyMortgage " data-button-xiti="form-mortgages-top"> <span>Estudiar hipoteca</span> </a> </span> <span class="contextual success icon-feedbk-ok applyMortgageDone d-none"> Solicitud recibida </span> </span> </div> <div class="info-features"> <span> Plaza para <span>coche pequeño</span> </span> <span> <span>9,15 m²</span> </span> </div> <div id="links" class="links-detail-info"> <a href="#" class="favorite-btn btn regular " data-role="add" data-text-add='Guardar<span class="hide-txt"> favorito</span>' data-text-remove='Favorito' data-markup="favourite" data-markup-add="favourite" data-markup-remove="remove-favourite"> <svg class="icon-heart" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <title>Guardar<span class="hide-txt"> favorito</span></title> <g id="svg-heart" stroke-width="1" fill-rule="evenodd"> <g id="Group" transform="translate(1.469388, 1.959184)"> <path d="M10.5494545,4.34654416 C13.0317121,-2.66965835 21.0989011,-0.423454109 21.0989011,5.63737113 C21.0989011,12.2223851 10.5494545,20.5714286 10.5494545,20.5714286 C10.5494545,20.5714286 0,12.2223851 0,5.63737113 C0,-0.423454109 8.0671916,-2.66965835 10.5494545,4.34654416 Z" id="fav-svg-path"></path> </g> </g> <g id="svg-heart-fill" stroke-width="1" fill-rule="evenodd"> <g id="Group" transform="translate(1.469388, 1.959184)"> <path d="M10.5494545,4.34654416 C13.0317121,-2.66965835 21.0989011,-0.423454109 21.0989011,5.63737113 C21.0989011,12.2223851 10.5494545,20.5714286 10.5494545,20.5714286 C10.5494545,20.5714286 0,12.2223851 0,5.63737113 C0,-0.423454109 8.0671916,-2.66965835 10.5494545,4.34654416 Z" id="fav-svg-fill"></path> </g> </g> </svg> <span>Guardar<span class="hide-txt"> favorito</span></span> </a> <a href="#" class="discard-btn icon-delete" data-role="add" data-text-add='Descartar' data-text-remove='Recuperar' data-markup="discard" data-markup-add="discard" data-markup-remove="recover"> <span>Descartar</span> </a> <a href="#" id="share-friend-link" class="icon-share" data-xiti-page="detalle::conversiones::form_compartir" data-markup="detalle::compartir::email"> <span>Compartir</span> </a> </div> <div id="comment-wrapper" class="comment-wrapper "> <span class="print-title">Nota personal</span> <div class="text-wrapper"> <span id="comment-text" class="comment-text"></span> <a class="edit-title-mobile">Editar</a> <a class="edit-title">Editar nota</a> </div> <div class="textarea-wrapper"> <span class="icon-note"></span> <textarea id="comment-textarea" class="comment-textarea " spellcheck="false" data-chars-counter="1000" maxlength="1000" placeholder="Escribe una nota personal (sólo tú podrás verla)"></textarea> </div> </div> <div class="commentsContainer" data-url="/ajax/comment.ajax"> <h2 class="txt-medium txt-bold mb-small">Comentario del anunciante</h2> <div class="select-comment-lang"> Disponible en: <a href="#" class="current" data-lang="es">Español</a> <a href="#" data-lang="ca" style="display:none" class="optionalLanguage">Català</a> <a href="#" data-lang="en">English</a> <a href="#" data-lang="fr" style="display:none" class="optionalLanguage">Français</a> <a href="#" data-lang="de" style="display:none" class="optionalLanguage">Deutsch</a> <a href="#" data-lang="it" style="display:none" class="optionalLanguage">Italiano</a> <a href="#" data-lang="pt" style="display:none" class="optionalLanguage">Português</a> <a href="#" data-lang="da" style="display:none" class="optionalLanguage">Dansk</a> <a href="#" data-lang="fi" style="display:none" class="optionalLanguage">Suomi</a> <a href="#" data-lang="no" style="display:none" class="optionalLanguage">Norsk</a> <a href="#" data-lang="nl" style="display:none" class="optionalLanguage">Nederlands</a> <a href="#" data-lang="pl" style="display:none" class="optionalLanguage">Polski</a> <a href="#" data-lang="ro" style="display:none" class="optionalLanguage">Română</a> <a href="#" data-lang="ru" style="display:none" class="optionalLanguage">русский</a> <a href="#" data-lang="sv" style="display:none" class="optionalLanguage">Svenska</a> <a href="#" data-lang="el" style="display:none" class="optionalLanguage">Ελληνικά</a> <a href="#" data-lang="zh" style="display:none" class="optionalLanguage">中文</a> </div> <div class="comment"> <div class="adCommentsLanguage expandable" data-compressed-max-length="1650"> "La plaza pertenece a un garaje muy amplio por el que se puede circular f&aacute;cilmente, ubicado en el Passatge de Sim&oacute;, en el barrio de la Sagrada Familia. Es un pasaje con un &uacute;nico carril y muy tranquilo, lo que hace que la entrada y salida sea muy sencilla. El garaje dispone de seguridad por la noche.<br/>La plaza est&aacute; en la planta -1, a escasos metros de la puerta autom&aacute;tica de acceso. De hecho, se puede desaparcar y salir del parking sin apenas maniobrar. La rampa de acceso es muy amplia (de dos carriles). Se trata de una plaza independiente, sin que colinde con ninguna otra, para coche peque&ntilde;o." </div> </div> <div class="spinnerTranslations" style="display: none"> <div class="googleTranslateLogo"> <img src="https://st1.idealista.com/static/common/release/detail/resources/img/loading.gif"> </div> </div> <div class="spinnerTranslationsMobile" style="display: none"> <div class="googleTranslateLogo"> <img src="https://st1.idealista.com/static/common/release/detail/resources/img/loading-mobile.gif"> </div> </div> <div class="feedback contextual full-width error icon-feedbk-ko translation-error" data-lang="es" style="display: none"> Lo sentimos, no es posible traducir el comentario en estos momentos. Prueba de nuevo más tarde. </div> <div class="feedback contextual full-width error icon-feedbk-ko translation-error" data-lang="ca" style="display: none"> Ho sentim, no és possible traduir el comentari en aquests moments. Prova de nou més tard. </div> <div class="feedback contextual full-width error icon-feedbk-ko translation-error" data-lang="en" style="display: none"> Sorry, we can't translate this description right now. Please try again later. </div> <div class="feedback contextual full-width error icon-feedbk-ko translation-error" data-lang="fr" style="display: none"> Nous sommes désolés, nous ne pouvons pas traduire de commentaire pour l'instant. Veuillez réessayer plus tard. </div> <div class="feedback contextual full-width error icon-feedbk-ko translation-error" data-lang="de" style="display: none"> Leider kann die Beschreibung im Augenblick nicht übersetzt werden. Bitte versuchen Sie es später noch einmal. </div> <div class="feedback contextual full-width error icon-feedbk-ko translation-error" data-lang="it" style="display: none"> Ci dispiace, al momento non è possibile tradurre il commento. Riprova più tardi. </div> <div class="feedback contextual full-width error icon-feedbk-ko translation-error" data-lang="pt" style="display: none"> Lamentamos, não é possível traduzir o comentário neste momento. Volta a tentar mais tarde. </div> <div class="feedback contextual full-width error icon-feedbk-ko translation-error" data-lang="da" style="display: none"> </div> <div class="feedback contextual full-width error icon-feedbk-ko translation-error" data-lang="fi" style="display: none"> Kommenttia ei valitettavasti voida tällä hetkellä kääntää. Yritä myöhemmin uudelleen. </div> <div class="feedback contextual full-width error icon-feedbk-ko translation-error" data-lang="no" style="display: none"> </div> <div class="feedback contextual full-width error icon-feedbk-ko translation-error" data-lang="nl" style="display: none"> Sorry, het is niet mogelijk om de reactie op dit moment te vertalen. Probeer het later opnieuw. </div> <div class="feedback contextual full-width error icon-feedbk-ko translation-error" data-lang="pl" style="display: none"> Niestety, obecnie nie można przetłumaczyć tego komentarza. Spróbuj ponownie później. </div> <div class="feedback contextual full-width error icon-feedbk-ko translation-error" data-lang="ro" style="display: none"> Ne pare rău, pentru moment, nu este posibilă traducerea comentariului. Încearcă din nou mai târziu. </div> <div class="feedback contextual full-width error icon-feedbk-ko translation-error" data-lang="ru" style="display: none"> К сожалению, перевод комментария в данный момент невозможен. Попробуйте еще раз позже. </div> <div class="feedback contextual full-width error icon-feedbk-ko translation-error" data-lang="sv" style="display: none"> Vi ber om ursäkt, det är inte möjligt att översätta den här kommentaren just nu. Försök igen senare. </div> <div class="feedback contextual full-width error icon-feedbk-ko translation-error" data-lang="el" style="display: none"> </div> <div class="feedback contextual full-width error icon-feedbk-ko translation-error" data-lang="zh" style="display: none"> </div> </div> <section id="details" class="details-box"> <div id="banners_16025" class="idealista-banner"> <p class="adv_txt"><span>Publicidad</span></p> <div id="sas_16025"></div> <script type="didomi/javascript" data-vendor= "iab:45">
var sas = sas || {};
sas.cmd = sas.cmd || [];
sas.cmd.push(function() {
if(sas.render) {
sas.render('16025');
}
});
</script> </div> <div class="details-property"> <div class="details-property-feature-one"> <h2 class="txt-medium txt-bold mb-small">Características básicas</h2> <div class="details-property_features"> <ul> <li>Plaza para coche pequeño</li> <li>9,15 m²</li> <li>Cubierta</li> </ul> </div> </div> <div class="details-property-feature-two"> <h2 class="txt-medium txt-bold mb-small">Extras</h2> <div class="details-property_features"> <ul> <li> Puerta automática de garaje </li> <li> Personal de seguridad </li> </ul> </div> </div> <div class="details-property-feature-three"> </div> </div> </section> <section class="details-box date-update-block"> <span class="icon-feedbk-info"></span> <p class="date-update-text">Anuncio actualizado hace 7 días</p> </section> </section> <div id="premium-multimedia-container" class="multimedia-content"> <div id="homestaging" role="img"></div> <div id="gallery" class="rs-gallery"></div> <div id="gallery" class="rs-gallery"></div> </div> <div id="multimedia-container" class="multimedia-content"> <div id="main-multimedia" class="images-slider"> </div> </div> <section id="mortgages" class="mortgages-content"> <div id="helpus-improve" class="ide-box-detail overlay-box"> <h2 class="txt-medium txt-bold mb-small icon-flag"> ¿Hay algún error en este anuncio? </h2> <p class="helpus-improve_txt"> Infórmanos para corregirlo y ayudar a otros usuarios. </p> <a class="helpus-improve_notify" href="#" data-xiti-page="detalle::conversiones::form-reportar-error" data-xiti-shared-n2="true" data-title="Cuéntanos qué error has visto"> Cuéntanos qué error has visto </a> </div> <div class="ide-box-detail overlay-box"> <div class="toggle-price"> <article class="price-feature clearfix"> <h2 class="txt-medium txt-bold">Precio</h2> <section class="price-features__container"> <p class="flex-feature"> <span class="txt-medium">Precio del inmueble:</span> <strong class="txt-medium">17.900 €</strong> </p> <p class="flex-feature"> <span class="txt-medium">Gastos de comunidad 20 €/mes</span> </p> <div> <p> <a href="#" class="counter-offer" data-xiti-page="detalle::conversiones::form_contraoferta" data-xiti-shared-n2="true" title='Hacer una contraoferta'> Hacer una contraoferta </a> </p> <p> <a id="alert-price-down-btn" class="favorite-btn" data-role="add" data-text-add="Recibir aviso si baja de precio" data-text-remove="¡Oído cocina! te avisaremos si baja de precio" data-xiti-click="detalle::conversiones::avisame_si_baja" data-xiti-shared-n2="true" data-targetId="13" title="¿Cuánto pagarías por este inmueble? recibir aviso si baja de precio">Recibir aviso si baja de precio</a> </p> </div> </section> </article> </div> </div> <div class="ide-box-detail overlay-box mortgages"> <article class="simulator clearfix"> <a class="mortgages__logo" data-markup="detalle::mortgage-logo" href="/hipotecas/"><span class="icon-font mb-regular"> <span class="icon-idealista-icon"></span> <span class="icon-hipotecas"></span> </span></a> <section id="simulator-container" class="col-ide-lg-8"> <div class="item-form flex-feature taxes"> <span class="txt-medium">Precio del inmueble: </span> <span class="txt-medium">17.900 €</span> </div> <div class="item-form js-taxes taxes flex-feature"> <span class="txt-medium">Impuestos y gastos: <a href="#" id="taxes-info" data-markup="detalle::tax-expenses" class="icon-feedbk-info"></a> </span> <span><span class="js-result-tmpl txt-medium" data-template="{{=expensesGlosary.totalExpenses}}">-</span> &euro;</span> </div> <div class="item-form js-price-with-expenses price-expenses flex-feature"> <span class="txt-medium">Precio + gastos: </span> <span class="txt-medium txt-bold"><span class="js-result-tmpl" data-template="{{=purchasePrice}}">-</span> &euro;</span> </div> <div class="item-form item-redils js-saving-slider savings"> <label><span class="txt-medium">Ahorro aportado</span></label> <div class="input-container"> <span class="symbol">&euro;</span> <input type="tel" name="givenAmount" maxlength="9" data-input/> <span class="percentage"> %</span> </div> <div data-slider></div> </div> <div class="feedback contextual full-width warning d-none" id="saving-warning"></div> <div class="feedback contextual full-width warning icon-feedbk-info d-none" id="saving-alert"></div> <div class="item-form item-redils js-term-slider"> <label><span class="txt-medium">Plazo en años</span></label> <div class="input-container short-value"> <input type="tel" name="years" maxlength="2" data-min="5" data-max="40" data-input/> </div> <div data-slider></div> </div> <div class="item-form item-redils simulator-rate"> <label> <span class="txt-medium">Tipo de interés <a href="#" id="rate-info" data-markup="detalle::interest" class="icon-feedbk-info"></a> </span> </label> <ul class="segmented-btn-group clearfix js-rate-type"> <li> <label class="input-radio"> <input name="interestType" type="radio" data-value="1"> <span><span class="txt-medium">Fijo</span></span> </label> </li> <li> <label class="input-radio"> <input name="interestType" type="radio" data-value="2" checked> <span><span class="txt-medium">Variable</span></span> </label> </li> </ul> <div class="item-form item-redils"> <div class="input-container short-value js-rate-input rate"> <span class="symbol">%</span> <input type="tel" name="taxRate" maxlength="5" data-number-input=""/> </div> </div> </div> <div class="item-form js-montly-rate monthly-rate"> <strong class="txt-medium txt-bold">Tu cuota mensual:</strong> <strong class="txt-medium txt-bold"><span class="js-result-tmpl" data-template="{{=monthlyRate}}">3.120</span> &euro;</strong> </div> <div class="item-form"> <a class="btn regular fullwidth applyMortgage" data-markup="form-mortgages-bottom">Analizar mi caso</a> <span> Estos resultados son orientativos, calculados con los números que has introducido. <a class="simulator-conditions" target="_blank" href="/hipotecas/condiciones-contratacion">Condiciones generales</a>. </span> </div> </section> </article> </div> <div class="ide-box-detail overlay-box"> <article class="clearfix data-feature"> <a class="data__logo" href="/data/"><span class="icon-font mb-regular"> <span class="icon-idealista-icon"></span> <span class="icon-data"></span> </span></a> <section class="data-features__container"> <p class="txt-medium txt-bold">¿Cuánto vale este inmueble?</p> <p>Te enviamos un informe con la estimación de precio para este inmueble y con información de la zona.</p> <p> <a class="" href="/flow/estimacion-de-precio?lang=es&adId=92174198" data-markup="detalle::link::product-1202">Comprar informe de valoración de precio</a> </p> </section> </article> </div> </section> <div id="mapWrapper" class="ide-box-detail overlay-box"> <div id="headerMap" class="clearfix"> <h2 class="txt-medium txt-bold mb-small">Ubicación</h2> <ul> <li> Passatge de Simó, 10 </li> <li> Barrio La Sagrada Família </li> <li> Distrito Eixample </li> <li> Barcelona </li> <li> Área de Barcelona, Barcelona </li> </ul> </div> <div id="map"></div> <div id="static-map-container"> <div id="static-map"><a href="#" class="showMap" data-markup-map-photo='detalle::map::photo'></a></div> <div id="static-map-text-link"><a href="#" class="showMap icon-search" data-markup-map-link='detalle::map::link'><span>Ampliar mapa</span></a></div> </div> </div> <a id="statsPosition" name="statsPosition"></a> <div id='stats' class="ide-box-detail overlay-box mb-jumbo"> <h2 class='txt-medium txt-bold mb-small'>Estadísticas</h2> <p>Anuncio actualizado el 20 de diciembre</p> <a href="#">Ver número de visitas y contactos de este anuncio</a> <div id="stop-fixed-contact-scroll"></div> </div> </main> <div class="idealista-banner-wrapper"> <div id="banners_16023" class="idealista-banner"> <p class="adv_txt"><span>Publicidad</span></p> <div id="sas_16023"></div> <script type="didomi/javascript" data-vendor= "iab:45">
var sas = sas || {};
sas.cmd = sas.cmd || [];
sas.cmd.push(function() {
if(sas.render) {
sas.render('16023');
}
});
</script> </div> </div> </div> </div> </div> <section class="links-block-home"> <div class="content"> <nav> <h3 class="h4-simulated"> ¿Buscas inmueble? </h3> <ul> <li> <a href="/" title="Buscador principal"> Buscador principal </a> </li> <li> <a href="/pisos-bancos" title="Pisos de bancos"> Pisos de bancos </a> </li> <li> <a href="/pisos-vpo" title="Vivienda protegida y VPO"> Vivienda protegida y VPO </a> </li> <li> <a href="/properties-for-sale-in-spain" title="Viviendas en venta en España"> Viviendas en venta en España </a> </li> <li> <a href="/long-term-rentals-in-spain" title="Viviendas en alquiler en España"> Viviendas en alquiler en España </a> </li> <li> <a href="/pisos-en-alquiler" title="Casas y pisos en alquiler"> Casas y pisos en alquiler </a> </li> <li> <a href="/pisos-compartidos" title="Pisos compartidos"> Pisos compartidos </a> </li> <li> <a href="https://es.rentalia.com/" title="Rentalia, alquiler vacacional"> Rentalia, alquiler vacacional </a> </li> <li> <a href="https://www.idealista.com/sala-de-prensa/informes-precio-vivienda/" title="Informe de precios"> Informe de precios </a> </li> <li> <a href="https://www.idealista.com/maps/" title="idealista maps"> idealista maps </a> </li> <li> <a href="/luxury-homes-spain" title="Pisos y casas de lujo en venta en España"> Pisos y casas de lujo en venta en España </a> </li> <li> <a href="/luxury-rentals-spain" title="Pisos y casas de lujo en alquiler en España"> Pisos y casas de lujo en alquiler en España </a> </li> </ul> </nav> <nav> <h3 class="h4-simulated"> ¿Tienes un inmueble? </h3> <ul> <li> <a href="/info/publicar-anuncio" title="Pon tu anuncio gratis"> Pon tu anuncio gratis </a> </li> <li> <a href="/particulares" title="Servicios para anunciantes particulares"> Servicios para anunciantes particulares </a> </li> <li> <a href="/info/mejorar-foto-video" title="Mejorar las fotos de tu anuncio"> Mejorar las fotos de tu anuncio </a> </li> <li> <a href="/certificadoenergetico/" title="Solicitar certificado energético"> Solicitar certificado energético </a> </li> <li> <a href="/contratos-de-alquiler" title="Crea gratis tu contrato de alquiler"> Crea gratis tu contrato de alquiler </a> </li> <li> <a href="https://www.idealista.com/labs/cartel-anuncio-piso.htm" title="Imprimir carteles de tu anuncio"> Imprimir carteles de tu anuncio </a> </li> <li> <a href="/valoracion-de-inmuebles/" title="Valora gratis cualquier inmueble"> Valora gratis cualquier inmueble </a> </li> <li> <a href="/que-inmobiliaria-elegir/" title="Vender tu piso con una agencia"> Vender tu piso con una agencia </a> </li> <li> <a href="/base-datos-inquilinos-morosos/como-incluir-morosos" title="Notificar inquilino moroso"> Notificar inquilino moroso </a> </li> </ul> </nav> <nav> <h3 class="h4-simulated"> ¿Eres profesional inmobiliario? </h3> <ul> <li> <a href="/nuevo-profesional" title="Publica tus inmuebles de profesional"> Publica tus inmuebles de profesional </a> </li> <li> <a href="/login" title="¿Ya eres cliente? Entra en tu cuenta"> ¿Ya eres cliente? Entra en tu cuenta </a> </li> <li> <a href="https://www.idealista.com/webinars/" title="Webinars de idealista"> Webinars de idealista </a> </li> <li> <a href="/info/publicidad" title="Publicidad en idealista"> Publicidad en idealista </a> </li> <li> <a href="/info/acuerdos-internacionales" title="Acuerdos internacionales"> Acuerdos internacionales </a> </li> <li> <a href="https://www.idealista.com/tools/servicios" title="idealista/tools: software de gestión para inmobiliarias"> idealista/tools: software de gestión para inmobiliarias </a> </li> <li> <a href="/tools/software-recomendado-para-inmobiliarias" title="Otros software recomendados"> Otros software recomendados </a> </li> <li> <a href="/base-datos-inquilinos-morosos/consultar-lista-morosos" title="Registro inquilinos morosos"> Registro inquilinos morosos </a> </li> <li> <a href="https://www.idealista.com/data/" title="idealista/data: tecnología para el análisis inmobiliario"> idealista/data: tecnología para el análisis inmobiliario </a> </li> <li> <a href="https://www.avaibook.com" title="AvaiBook: software para gestionar tu alquiler vacacional"> AvaiBook: software para gestionar tu alquiler vacacional </a> </li> </ul> </nav> </div> </div> </section> <footer class="main-footer"> <div class="content"> <span role="img" class="icon-idealista-icon"></span> <div class="lang-selector lang-responsive"> <span class="lang-selector--lang-selected icon-arrow-dropdown"> <span class="flag lang-es"></span> <span class="lang-text">Español</span> </span> <ul class="lang-selector--lang-options" data-url-attribute="/user/update-user-language" data-user-authenticated="false"> <li> <a rel="nofollow" id="idioma-ca" value="ca" href="/ca/inmueble/92174198/" data-markup="cambio::idioma::pie::ca"> <span> <span role="img" class="flag lang-ca"></span>Català </span> </a> </li> <li> <a rel="nofollow" id="idioma-en" value="en" href="/en/inmueble/92174198/" data-markup="cambio::idioma::pie::en"> <span> <span role="img" class="flag lang-en"></span>English </span> </a> </li> <li> <a rel="nofollow" id="idioma-fr" value="fr" href="/fr/inmueble/92174198/" data-markup="cambio::idioma::pie::fr"> <span> <span role="img" class="flag lang-fr"></span>Français </span> </a> </li> <li> <a rel="nofollow" id="idioma-de" value="de" href="/de/inmueble/92174198/" data-markup="cambio::idioma::pie::de"> <span> <span role="img" class="flag lang-de"></span>Deutsch </span> </a> </li> <li> <a rel="nofollow" id="idioma-it" value="it" href="/it/inmueble/92174198/" data-markup="cambio::idioma::pie::it"> <span> <span role="img" class="flag lang-it"></span>Italiano </span> </a> </li> <li> <a rel="nofollow" id="idioma-pt" value="pt" href="/pt/inmueble/92174198/" data-markup="cambio::idioma::pie::pt"> <span> <span role="img" class="flag lang-pt"></span>Português </span> </a> </li> <li> <a rel="nofollow" id="idioma-da" value="da" href="/da/inmueble/92174198/" data-markup="cambio::idioma::pie::da"> <span> <span role="img" class="flag lang-da"></span>Dansk </span> </a> </li> <li> <a rel="nofollow" id="idioma-fi" value="fi" href="/fi/inmueble/92174198/" data-markup="cambio::idioma::pie::fi"> <span> <span role="img" class="flag lang-fi"></span>Suomi </span> </a> </li> <li> <a rel="nofollow" id="idioma-no" value="no" href="/no/inmueble/92174198/" data-markup="cambio::idioma::pie::no"> <span> <span role="img" class="flag lang-no"></span>Norsk </span> </a> </li> <li> <a rel="nofollow" id="idioma-nl" value="nl" href="/nl/inmueble/92174198/" data-markup="cambio::idioma::pie::nl"> <span> <span role="img" class="flag lang-nl"></span>Nederlands </span> </a> </li> <li> <a rel="nofollow" id="idioma-pl" value="pl" href="/pl/inmueble/92174198/" data-markup="cambio::idioma::pie::pl"> <span> <span role="img" class="flag lang-pl"></span>Polski </span> </a> </li> <li> <a rel="nofollow" id="idioma-ro" value="ro" href="/ro/inmueble/92174198/" data-markup="cambio::idioma::pie::ro"> <span> <span role="img" class="flag lang-ro"></span>Română </span> </a> </li> <li> <a rel="nofollow" id="idioma-ru" value="ru" href="/ru/inmueble/92174198/" data-markup="cambio::idioma::pie::ru"> <span> <span role="img" class="flag lang-ru"></span>Русский </span> </a> </li> <li> <a rel="nofollow" id="idioma-sv" value="sv" href="/sv/inmueble/92174198/" data-markup="cambio::idioma::pie::sv"> <span> <span role="img" class="flag lang-sv"></span>Svenska </span> </a> </li> <li> <a rel="nofollow" id="idioma-el" value="el" href="/el/inmueble/92174198/" data-markup="cambio::idioma::pie::el"> <span> <span role="img" class="flag lang-el"></span>Ελληνικά </span> </a> </li> <li> <a rel="nofollow" id="idioma-zh" value="zh" href="/zh/inmueble/92174198/" data-markup="cambio::idioma::pie::zh"> <span> <span role="img" class="flag lang-zh"></span>中文 </span> </a> </li> </ul> </div> <div class="footer-links-about"> <nav> <h3>Sobre idealista</h3> <ul> <li> <a rel='nofollow' href="https://www.idealista.com/sala-de-prensa/sobre-nosotros/" title="Quiénes somos"> Quiénes somos </a> </li> <li> <a rel='nofollow' href="https://www.idealista.com/sala-de-prensa/" title="Sala de prensa"> Sala de prensa </a> </li> <li> <a rel='nofollow' href="/info/trabaja-con-nosotros" title="Trabaja con nosotros"> Trabaja con nosotros </a> </li> <li> <a href="https://www.idealista.com/hipotecas/" title="idealista/hipotecas"> idealista/hipotecas </a> </li> <li> <a href="https://www.idealista.com/data/" title="idealista/data"> idealista/data </a> </li> <li> <a href="https://www.idealista.com/news/" title="idealista/news"> idealista/news </a> </li> <li> <a href="/labs/" title="idealista/labs"> idealista/labs </a> </li> </ul> </nav> <nav> <h3>Ayuda</h3> <ul> <li> <a rel='nofollow' href="https://www.idealista.com/ayuda/" title="Preguntas frecuentes (FAQs)"> Preguntas frecuentes (FAQs) </a> </li> <li> <a rel='nofollow' href="/info/contacto" title="Contacta con idealista"> Contacta con idealista </a> </li> <li> <a rel='nofollow' href="https://www.idealista.com/ayuda/articulos/politica-de-privacidad/" title="Política de privacidad"> Política de privacidad </a> </li> <li> <a rel='nofollow' href="/info/politica-cookies" title="Política de cookies"> Política de cookies </a> </li> <li> <a rel='nofollow' href="https://www.idealista.com/ayuda/articulos/terminos-y-condiciones-generales-de-idealista/" title="Condiciones generales"> Condiciones generales </a> </li> </ul> </nav> <nav> <h3> Otros países </h3> <ul> <li> <a rel='nofollow' href="https://www.idealista.it/es/" title="idealista Italia"> idealista Italia </a> </li> <li> <a rel='nofollow' href="https://www.idealista.pt/es/" title="idealista Portugal"> idealista Portugal </a> </li> </ul> </nav> </div> <div class="footer-links-social"> <div class="social-stores-links"> <h3> En tu móvil o tablet </h3> <nav class="app-stores"> <a href="https://itunes.apple.com/es/app/idealista.com/id465958311?mt=8" title="App Store" class="icon-itunes-link"></a> <a href="https://play.google.com/store/apps/details?id=com.idealista.android&hl=es" title="Google Play" class="icon-android-link"></a> </nav> <nav class="social-links"> <a href="https://www.facebook.com/idealista" title="Síguenos en Facebook" class="icon-facebook"> <span>Síguenos en Facebook</span> </a> <a href="https://twitter.com/idealista" title="Noticias inmobiliarias en Twitter" class="icon-twitter-figure"> <span>Noticias inmobiliarias en Twitter</span> </a> <a href="https://www.youtube.com/idealistavideos" title="Noticias inmobiliarias en Youtube" class="icon-youtube"> <span>Noticias inmobiliarias en Youtube</span> </a> <a href="https://instagram.com/idealistacom" title="Síguenos en Instagram" class="icon-instagram-figure"> <span>Síguenos en Instagram</span> </a> </nav> </div> <strong>idealista</strong> <span class="ide-copyright"> Copyright &copy; 2000-2020 </span> </div> </div> </footer> <script>
var smartBannerConfig={activated:false,locale:"es",userAuthenticated:false,urls:{urlLegalNotice:"/info/politica-cookies"}};
</script> <script type="text/javascript">
var communicatorConfig={enabled:true,endPointURL:"//commt.idealista.com",authCookieName:"cc",refreshTokenURL:"/communicator/refresh-token.ajax"};
</script> <script>
var config={locale:"es",userAuthenticated:false,propertyId:92174198,favoriteFirstTime:true,discardFirstTime:true,urlAddFavorite:"/add-favorite.htm",urlAdNewContactInfo:"/es/ajax/listingController/adContactInfoForDetail.ajax",addFavoriteTargetId:"32",discardTargetId:"39",viewStatisticsTargetId:"40",urlRemoveFavorite:"/remove-favorite.htm",urlAddDiscarded:"/add-ruled-out-detail.htm",urlRemoveDiscarded:"/remove-ruled-out.htm",urlAddComment:"/update-favorite-comment.htm",urlContact:"/ajax/contact/2/sendcontact.ajax",urlCounterOffer:"/ajax/contact/suggested/4/sendcontact.ajax",urlContactDetailGallery:"/ajax/contact/42/sendcontact.ajax",urlContactDetailGalleryVirtualTour:"/ajax/contact/suggested/sendcontact.ajax",urlContactDetailGalleryFloorPlan:"/ajax/contact/45/sendcontact.ajax",urlContactDetailGallery3DTour:"/ajax/contact/46/sendcontact.ajax",urlContactDetailGalleryVideo:"/ajax/contact/47/sendcontact.ajax",urlSuggestedAgencies:"/ajax/zoneexperts/suggested/2/sendcontact.ajax",urlReloadCaptcha:"/ajax/captcha/reloadCaptcha.ajax",urlVirtualTour:"",urlAlertSummary:"/ajax/alertsummary.ajax/25",mediaTablet:"screen and (max-device-width: 1023px) and (min-device-width: 767px), screen and (max-width: 1023px) and (min-width: 767px)",mediaMobile:"screen and (max-width: 767px)",mediaDesktop:"screen and (min-width: 1024px) and (min-device-width: 1024px)",mediaLandscape:"screen and (orientation: landscape)",mortgagesMinSavingsToContact:7,showLightboxPosition:null,showLightboxVirtualTour:false,detailUrl:"/inmueble/92174198/",imageSizes:{"1280X400":80,"140X105":80,"140":80,"300":85,"600":80,"850":80,"1500":80,"250X188":80,"500X375":80,"300X225":80,"600X450":80},maxAdContactMessagesSuggested:3,openContactModal:false,showAdIncidenceForm:false,urlContactMortgages:"/ajax/contact-mortgages.ajax?xtatc=[detalle_solicitar_hipoteca]",xitiClientMarkup:{detailIncidenceSendForm:"detalle::conversiones::form-reportar-error",detailContactForm:"detalle::conversiones::form_contacto",detailFavoritesLoginInPlace:"detalle::conversiones::login-favoritear-inplace",detailRuledOutLoginInPlace:"detalle::conversiones::login-descartar-inplace"},contactInGalleryLightboxTitle:"Contactar",contactInGalleryFormTitle:"¿Te ha gustado?",urlCreateUserAskingStats:"/new-user-asking-stats.ajax",showMorePhotos:{desktop:{position:4,threshold:4,},mobile:{position:3,threshold:3,}},phoneIntlConfig:{initialCountry:"es",preferredCountries:[],allCountries:[]},urlCalculateUCISavings:"/ajax/calculate-savings-form",urlConversations:"",currentSearchUrl:"/venta-garajes/barcelona/eixample/la-sagrada-familia/",urlResultsFromComparator:"/ajax/retrieve-comparator-results.ajax",urlResultsFromValidationStep1:"/hipotecas/validate-mortgages-contact-step1.ajax",urlResultsFromValidationStep2:"/hipotecas/validate-mortgages-contact-step2.ajax",staticsBaseUri:"https://st1.idealista.com/static",galleryReactToggle:true,isFavourite:false,showContact:true,detailWithSuggestionsToggle:true,hasToShowRecommendations:false};var microsoft_key="";var mapConfig={latitude:"41.4042378",longitude:"2.1732132",onMapElements:false,markerType:0,markerVisible:1,i18nDetail:"",versionId:"3.30",language:"es",clientId:"gme-idealistalibertad1",dataProvider:"googlev3",channel:"map_detail"};var adMultimediasInfo={fullScreenGalleryPics:[{height:768,width:1024,multimediaId:832773863,title:"Imagen Plaza de parking de garaje en Passatge de Simó, 10, La Sagrada Família, Barcelona",imageUrl:"/inmueble/92174198/foto/1/",absolutePosition:1,shortImageUrl:false,aspectRatioId:1,hoverText:"Plaza de parking",imageDataService:"https://img3.idealista.com/blur/WEB_DETAIL/0/id.pro.es.image.master/23/bc/87/832773863.jpg,WEB_DETAIL",isPlan:false,tag:"Plaza de parking"},{height:768,width:1024,multimediaId:832773866,title:"Imagen Plaza de parking de garaje en Passatge de Simó, 10, La Sagrada Família, Barcelona",imageUrl:"/inmueble/92174198/foto/2/",absolutePosition:2,shortImageUrl:false,aspectRatioId:1,hoverText:"Plaza de parking",imageDataService:"https://img3.idealista.com/blur/WEB_DETAIL/0/id.pro.es.image.master/b0/fe/e2/832773866.jpg,WEB_DETAIL",isPlan:false,tag:"Plaza de parking"},{height:1125,width:1500,multimediaId:832773864,title:"Imagen Vistas de garaje en Passatge de Simó, 10, La Sagrada Família, Barcelona",imageUrl:"/inmueble/92174198/foto/3/",absolutePosition:3,shortImageUrl:false,aspectRatioId:1,hoverText:"Vistas",imageDataService:"https://img3.idealista.com/blur/WEB_DETAIL/0/id.pro.es.image.master/e8/ab/07/832773864.jpg,WEB_DETAIL",isPlan:false,tag:"Vistas"},{height:1500,width:1125,multimediaId:832773865,title:"Imagen Vistas de garaje en Passatge de Simó, 10, La Sagrada Família, Barcelona",imageUrl:"/inmueble/92174198/foto/4/",absolutePosition:4,shortImageUrl:false,aspectRatioId:2,hoverText:"Vistas",imageDataService:"https://img3.idealista.com/blur/WEB_DETAIL/0/id.pro.es.image.master/52/b6/31/832773865.jpg,WEB_DETAIL",isPlan:false,tag:"Vistas"}],verticalGalleryPics:[{height:768,width:1024,multimediaId:832773866,title:"Imagen Plaza de parking de garaje en Passatge de Simó, 10, La Sagrada Família, Barcelona",imageUrl:"/inmueble/92174198/foto/2/",absolutePosition:2,shortImageUrl:false,aspectRatioId:1,hoverText:"Plaza de parking",imageDataService:"https://img3.idealista.com/blur/WEB_DETAIL/0/id.pro.es.image.master/b0/fe/e2/832773866.jpg,WEB_DETAIL",isPlan:false,tag:"Plaza de parking"},{height:1125,width:1500,multimediaId:832773864,title:"Imagen Vistas de garaje en Passatge de Simó, 10, La Sagrada Família, Barcelona",imageUrl:"/inmueble/92174198/foto/3/",absolutePosition:3,shortImageUrl:false,aspectRatioId:1,hoverText:"Vistas",imageDataService:"https://img3.idealista.com/blur/WEB_DETAIL/0/id.pro.es.image.master/e8/ab/07/832773864.jpg,WEB_DETAIL",isPlan:false,tag:"Vistas"},{height:1500,width:1125,multimediaId:832773865,title:"Imagen Vistas de garaje en Passatge de Simó, 10, La Sagrada Família, Barcelona",imageUrl:"/inmueble/92174198/foto/4/",absolutePosition:4,shortImageUrl:false,aspectRatioId:2,hoverText:"Vistas",imageDataService:"https://img3.idealista.com/blur/WEB_DETAIL/0/id.pro.es.image.master/52/b6/31/832773865.jpg,WEB_DETAIL",isPlan:false,tag:"Vistas"}],multimediaGalleryMarkup:"detalle::foto",mainPromoPic:false,mainPic:{height:768,width:1024,multimediaId:832773863,title:"Imagen Plaza de parking de garaje en Passatge de Simó, 10, La Sagrada Família, Barcelona",imageUrl:"/inmueble/92174198/foto/1/",absolutePosition:1,shortImageUrl:false,aspectRatioId:1,hoverText:"Plaza de parking",imageDataService:"https://img3.idealista.com/blur/WEB_DETAIL_TOP/0/id.pro.es.image.master/23/bc/87/832773863.jpg,WEB_DETAIL_TOP",isPlan:false,tag:"Plaza de parking"},fakeAnchorsObject:{"no-pics":{text:"fotos","no-pics":4}},homeStaging:[]};var adDetail={headerTitle:"Garaje en venta en Passatge de Simó, 10",headerFeatures:[{text:"17.900",textLink:null,textTitle:null,url:null,targetId:null,typeLink:"INTERNAL_URL",urlDescription:null,xitiClickName:null,xitiNavigationName:null,subText:"",featureName:"PRICE",featureValue:null,classesToAppend:null,isRecentAd:null,notSharedXitiN2:false,subItems:[]},{text:"Plaza para",textLink:null,textTitle:null,url:null,targetId:null,typeLink:"INTERNAL_URL",urlDescription:null,xitiClickName:null,xitiNavigationName:null,subText:"coche pequeño",featureName:"GARAGE_TYPE",featureValue:null,classesToAppend:null,isRecentAd:null,notSharedXitiN2:false,subItems:[]},{text:"9,15 m²",textLink:null,textTitle:null,url:null,targetId:null,typeLink:"INTERNAL_URL",urlDescription:null,xitiClickName:null,xitiNavigationName:null,subText:null,featureName:"CONSTRUCTED_AREA",featureValue:null,classesToAppend:null,isRecentAd:null,notSharedXitiN2:false,subItems:[]}]};
</script> <script>
var mortgagesConfig={countries:[],simulateSavingsURL:"/ajax/calculate-savings-form",initialPrice:17900,savedMoney:5370,rateInit:0.89,locationId:"0-EU-ES-08",defaultPercentSavings:30,propertyTypeId:1,maxYears:40,limitYears:30,yearsSupported:30,taxRateVariable:0.89,taxRateFixed:1.9,taxRateFixedRanges:[],simulationWithExpenses:true,calculationType:"MAX_PERCENTAGE_SAVINGS_CALCULATOR",expensesCalculationType:"MIN_FIXED_SAVINGS_CALCULATOR",buyingType:1,debounceMillis:300,termYears:30,markup_params:{read_more:{"read-more":"detail::mortgage-simulation::read-info"},read_close:{"read-more":"detail::mortgage-simulation::close-info"}},interestType:2,simulationType:1,offsetPercentSavings:0};mortgagesConfig.taxRateFixedRanges.push({min:0,max:10,rate:1.5});mortgagesConfig.taxRateFixedRanges.push({min:0,max:10,rate:1.5});mortgagesConfig.taxRateFixedRanges.push({min:11,max:15,rate:1.55});mortgagesConfig.taxRateFixedRanges.push({min:11,max:15,rate:1.55});mortgagesConfig.taxRateFixedRanges.push({min:16,max:20,rate:1.9});mortgagesConfig.taxRateFixedRanges.push({min:16,max:20,rate:1.9});mortgagesConfig.taxRateFixedRanges.push({min:21,max:25,rate:2.15});mortgagesConfig.taxRateFixedRanges.push({min:21,max:25,rate:2.15});mortgagesConfig.taxRateFixedRanges.push({min:26,max:40,rate:2.25});mortgagesConfig.taxRateFixedRanges.push({min:26,max:40,rate:2.25});mortgagesConfig.taxRateFixedRanges.push({min:0,max:10,rate:1.5});mortgagesConfig.taxRateFixedRanges.push({min:11,max:15,rate:1.55});mortgagesConfig.taxRateFixedRanges.push({min:16,max:20,rate:1.9});mortgagesConfig.taxRateFixedRanges.push({min:21,max:25,rate:2.15});mortgagesConfig.taxRateFixedRanges.push({min:26,max:40,rate:2.25});mortgagesConfig.countries.push({id:"0-AS-AF",name:"Afganistán"});mortgagesConfig.countries.push({id:"0-EU-AL",name:"Albania"});mortgagesConfig.countries.push({id:"0-EU-DE",name:"Alemania"});mortgagesConfig.countries.push({id:"0-EU-AD",name:"Andorra"});mortgagesConfig.countries.push({id:"0-AF-AO",name:"Angola"});mortgagesConfig.countries.push({id:"0-AM-AI",name:"Anguila"});mortgagesConfig.countries.push({id:"0-AM-AG",name:"Antigua y Barbuda"});mortgagesConfig.countries.push({id:"0-AM-AN",name:"Antillas Neerlandesas"});mortgagesConfig.countries.push({id:"0-AS-SA",name:"Arabia Saudí"});mortgagesConfig.countries.push({id:"0-AF-DZ",name:"Argelia"});mortgagesConfig.countries.push({id:"0-AM-AR",name:"Argentina"});mortgagesConfig.countries.push({id:"0-AS-AM",name:"Armenia"});mortgagesConfig.countries.push({id:"0-AM-AW",name:"Aruba"});mortgagesConfig.countries.push({id:"0-OC-AU",name:"Australia"});mortgagesConfig.countries.push({id:"0-EU-AT",name:"Austria"});mortgagesConfig.countries.push({id:"0-AS-AZ",name:"Azerbaiján"});mortgagesConfig.countries.push({id:"0-AM-BS",name:"Bahamas"});mortgagesConfig.countries.push({id:"0-AS-BD",name:"Bangladés"});mortgagesConfig.countries.push({id:"0-AM-BB",name:"Barbados"});mortgagesConfig.countries.push({id:"0-AS-BH",name:"Baréin"});mortgagesConfig.countries.push({id:"0-EU-BE",name:"Bélgica"});mortgagesConfig.countries.push({id:"0-AM-BZ",name:"Belice"});mortgagesConfig.countries.push({id:"0-AF-BJ",name:"Benín"});mortgagesConfig.countries.push({id:"0-AM-BM",name:"Bermudas"});mortgagesConfig.countries.push({id:"0-EU-BY",name:"Bielorrusia "});mortgagesConfig.countries.push({id:"0-AM-BO",name:"Bolivia"});mortgagesConfig.countries.push({id:"0-EU-BA",name:"Bosnia y Herzegovina"});mortgagesConfig.countries.push({id:"0-AF-BW",name:"Botsuana"});mortgagesConfig.countries.push({id:"0-AM-BR",name:"Brasil"});mortgagesConfig.countries.push({id:"0-AS-BN",name:"Brunéi"});mortgagesConfig.countries.push({id:"0-EU-BG",name:"Bulgaria"});mortgagesConfig.countries.push({id:"0-AF-BF",name:"Burkina Faso"});mortgagesConfig.countries.push({id:"0-AF-BI",name:"Burundi"});mortgagesConfig.countries.push({id:"0-AS-BT",name:"Bután"});mortgagesConfig.countries.push({id:"0-AF-CV",name:"Cabo Verde"});mortgagesConfig.countries.push({id:"0-AS-KH",name:"Camboya"});mortgagesConfig.countries.push({id:"0-AF-CM",name:"Camerún"});mortgagesConfig.countries.push({id:"0-AM-CA",name:"Canadá"});mortgagesConfig.countries.push({id:"0-AF-TD",name:"Chad"});mortgagesConfig.countries.push({id:"0-AM-CL",name:"Chile"});mortgagesConfig.countries.push({id:"0-AS-CN",name:"China"});mortgagesConfig.countries.push({id:"0-AS-CY",name:"Chipre"});mortgagesConfig.countries.push({id:"0-EU-VA",name:"Ciudad del Vaticano"});mortgagesConfig.countries.push({id:"0-AM-CO",name:"Colombia"});mortgagesConfig.countries.push({id:"0-AF-KM",name:"Comoras"});mortgagesConfig.countries.push({id:"0-AF-CG",name:"Congo"});mortgagesConfig.countries.push({id:"0-AS-KP",name:"Corea del Norte"});mortgagesConfig.countries.push({id:"0-AS-KR",name:"Corea del Sur"});mortgagesConfig.countries.push({id:"0-AM-CR",name:"Costa Rica"});mortgagesConfig.countries.push({id:"0-AF-CI",name:"Costa de Marfil"});mortgagesConfig.countries.push({id:"0-EU-HR",name:"Croacia"});mortgagesConfig.countries.push({id:"0-AM-CU",name:"Cuba"});mortgagesConfig.countries.push({id:"0-EU-DK",name:"Dinamarca"});mortgagesConfig.countries.push({id:"0-AM-DM",name:"Dominica"});mortgagesConfig.countries.push({id:"0-AM-EC",name:"Ecuador"});mortgagesConfig.countries.push({id:"0-AF-EG",name:"Egipto"});mortgagesConfig.countries.push({id:"0-AM-SV",name:"El Salvador"});mortgagesConfig.countries.push({id:"0-AS-AE",name:"Emiratos Árabes Unidos"});mortgagesConfig.countries.push({id:"0-AF-ER",name:"Eritrea"});mortgagesConfig.countries.push({id:"0-EU-SK",name:"Eslovaquia"});mortgagesConfig.countries.push({id:"0-EU-SI",name:"Eslovenia"});mortgagesConfig.countries.push({id:"0-EU-ES",name:"España"});mortgagesConfig.countries.push({id:"0-AM-US",name:"Estados Unidos"});mortgagesConfig.countries.push({id:"0-EU-EE",name:"Estonia"});mortgagesConfig.countries.push({id:"0-AF-SZ",name:"Esuatini"});mortgagesConfig.countries.push({id:"0-AF-ET",name:"Etiopía"});mortgagesConfig.countries.push({id:"0-AS-PH",name:"Filipinas"});mortgagesConfig.countries.push({id:"0-EU-FI",name:"Finlandia"});mortgagesConfig.countries.push({id:"0-OC-FJ",name:"Fiyi"});mortgagesConfig.countries.push({id:"0-EU-FR",name:"Francia"});mortgagesConfig.countries.push({id:"0-AF-GA",name:"Gabón"});mortgagesConfig.countries.push({id:"0-AF-GM",name:"Gambia"});mortgagesConfig.countries.push({id:"0-AS-GE",name:"Georgia"});mortgagesConfig.countries.push({id:"0-AF-GH",name:"Ghana"});mortgagesConfig.countries.push({id:"0-EU-GI",name:"Gibraltar"});mortgagesConfig.countries.push({id:"0-AM-GD",name:"Granada"});mortgagesConfig.countries.push({id:"0-EU-GR",name:"Grecia"});mortgagesConfig.countries.push({id:"0-AM-GL",name:"Groenlandia"});mortgagesConfig.countries.push({id:"0-AM-GP",name:"Guadalupe"});mortgagesConfig.countries.push({id:"0-OC-GU",name:"Guam"});mortgagesConfig.countries.push({id:"0-AM-GT",name:"Guatemala"});mortgagesConfig.countries.push({id:"0-AM-GF",name:"Guayana Francesa"});mortgagesConfig.countries.push({id:"0-EU-GG",name:"Guernesey"});mortgagesConfig.countries.push({id:"0-AF-GN",name:"Guinea"});mortgagesConfig.countries.push({id:"0-AF-GQ",name:"Guinea Ecuatorial"});mortgagesConfig.countries.push({id:"0-AF-GW",name:"Guinea-Bisáu"});mortgagesConfig.countries.push({id:"0-AM-GY",name:"Guyana"});mortgagesConfig.countries.push({id:"0-AM-HT",name:"Haití"});mortgagesConfig.countries.push({id:"0-AM-HN",name:"Honduras"});mortgagesConfig.countries.push({id:"0-EU-HU",name:"Hungría"});mortgagesConfig.countries.push({id:"0-AS-IN",name:"India"});mortgagesConfig.countries.push({id:"0-AS-ID",name:"Indonesia"});mortgagesConfig.countries.push({id:"0-AS-IQ",name:"Irak"});mortgagesConfig.countries.push({id:"0-AS-IR",name:"Irán"});mortgagesConfig.countries.push({id:"0-EU-IE",name:"Irlanda"});mortgagesConfig.countries.push({id:"0-AS-CX",name:"Isla Christmas"});mortgagesConfig.countries.push({id:"0-OC-NF",name:"Isla Norfolk"});mortgagesConfig.countries.push({id:"0-EU-IM",name:"Isla de Man"});mortgagesConfig.countries.push({id:"0-EU-IS",name:"Islandia"});mortgagesConfig.countries.push({id:"0-AM-KY",name:"Islas Caimán"});mortgagesConfig.countries.push({id:"0-AS-CC",name:"Islas Cocos"});mortgagesConfig.countries.push({id:"0-OC-CK",name:"Islas Cook"});mortgagesConfig.countries.push({id:"0-EU-FO",name:"Islas Feroe"});mortgagesConfig.countries.push({id:"0-AM-FK",name:"Islas Malvinas"});mortgagesConfig.countries.push({id:"0-OC-MP",name:"Islas Marianas del Norte"});mortgagesConfig.countries.push({id:"0-OC-MH",name:"Islas Marshall"});mortgagesConfig.countries.push({id:"0-OC-PN",name:"Islas Pitcairn"});mortgagesConfig.countries.push({id:"0-OC-SB",name:"Islas Salomón"});mortgagesConfig.countries.push({id:"0-AM-TC",name:"Islas Turcas y Caicos"});mortgagesConfig.countries.push({id:"0-AM-VI",name:"Islas Vírgenes "});mortgagesConfig.countries.push({id:"0-AM-VG",name:"Islas Vírgenes Británicas"});mortgagesConfig.countries.push({id:"0-AS-IL",name:"Israel"});mortgagesConfig.countries.push({id:"0-EU-IT",name:"Italia"});mortgagesConfig.countries.push({id:"0-AM-JM",name:"Jamaica"});mortgagesConfig.countries.push({id:"0-AS-JP",name:"Japón"});mortgagesConfig.countries.push({id:"0-EU-JE",name:"Jersey"});mortgagesConfig.countries.push({id:"0-AS-JO",name:"Jordania"});mortgagesConfig.countries.push({id:"0-AS-KZ",name:"Kazajistán"});mortgagesConfig.countries.push({id:"0-AF-KE",name:"Kenia"});mortgagesConfig.countries.push({id:"0-AS-KG",name:"Kirguistán"});mortgagesConfig.countries.push({id:"0-OC-KI",name:"Kiribati"});mortgagesConfig.countries.push({id:"0-AS-KW",name:"Kuwait"});mortgagesConfig.countries.push({id:"0-AS-LA",name:"Laos"});mortgagesConfig.countries.push({id:"0-AF-LS",name:"Lesoto"});mortgagesConfig.countries.push({id:"0-EU-LV",name:"Letonia"});mortgagesConfig.countries.push({id:"0-AS-LB",name:"Líbano"});mortgagesConfig.countries.push({id:"0-AF-LR",name:"Liberia"});mortgagesConfig.countries.push({id:"0-AF-LY",name:"Libia"});mortgagesConfig.countries.push({id:"0-EU-LI",name:"Liechtenstein"});mortgagesConfig.countries.push({id:"0-EU-LT",name:"Lituania"});mortgagesConfig.countries.push({id:"0-EU-LU",name:"Luxemburgo"});mortgagesConfig.countries.push({id:"0-EU-MK",name:"Macedonia del Norte"});mortgagesConfig.countries.push({id:"0-AF-MG",name:"Madagascar"});mortgagesConfig.countries.push({id:"0-AS-MY",name:"Malasia"});mortgagesConfig.countries.push({id:"0-AF-MW",name:"Malaui"});mortgagesConfig.countries.push({id:"0-AS-MV",name:"Maldivas"});mortgagesConfig.countries.push({id:"0-AF-ML",name:"Mali"});mortgagesConfig.countries.push({id:"0-EU-MT",name:"Malta"});mortgagesConfig.countries.push({id:"0-AF-MA",name:"Marruecos"});mortgagesConfig.countries.push({id:"0-AM-MQ",name:"Martinica"});mortgagesConfig.countries.push({id:"0-AF-MU",name:"Mauricio"});mortgagesConfig.countries.push({id:"0-AF-MR",name:"Mauritania"});mortgagesConfig.countries.push({id:"0-AF-YT",name:"Mayotte"});mortgagesConfig.countries.push({id:"0-AM-MX",name:"México"});mortgagesConfig.countries.push({id:"0-OC-FM",name:"Micronesia"});mortgagesConfig.countries.push({id:"0-EU-MD",name:"Moldavia"});mortgagesConfig.countries.push({id:"0-EU-MC",name:"Mónaco"});mortgagesConfig.countries.push({id:"0-AS-MN",name:"Mongolia"});mortgagesConfig.countries.push({id:"0-EU-ME",name:"Montenegro"});mortgagesConfig.countries.push({id:"0-AM-MS",name:"Montserrat"});mortgagesConfig.countries.push({id:"0-AF-MZ",name:"Mozambique"});mortgagesConfig.countries.push({id:"0-AS-MM",name:"Myanmar/Birmania"});mortgagesConfig.countries.push({id:"0-AF-NA",name:"Namibia"});mortgagesConfig.countries.push({id:"0-OC-NR",name:"Nauru"});mortgagesConfig.countries.push({id:"0-AS-NP",name:"Nepal"});mortgagesConfig.countries.push({id:"0-AM-NI",name:"Nicaragua"});mortgagesConfig.countries.push({id:"0-AF-NE",name:"Níger"});mortgagesConfig.countries.push({id:"0-AF-NG",name:"Nigeria"});mortgagesConfig.countries.push({id:"0-OC-NU",name:"Niue"});mortgagesConfig.countries.push({id:"0-EU-NO",name:"Noruega"});mortgagesConfig.countries.push({id:"0-OC-NC",name:"Nueva Caledonia"});mortgagesConfig.countries.push({id:"0-OC-NZ",name:"Nueva Zelanda"});mortgagesConfig.countries.push({id:"0-AS-OM",name:"Omán"});mortgagesConfig.countries.push({id:"0-EU-NL",name:"Países Bajos"});mortgagesConfig.countries.push({id:"0-AS-PK",name:"Pakistán"});mortgagesConfig.countries.push({id:"0-OC-PW",name:"Palaos"});mortgagesConfig.countries.push({id:"0-AM-PA",name:"Panamá"});mortgagesConfig.countries.push({id:"0-OC-PG",name:"Papúa Nueva Guinea"});mortgagesConfig.countries.push({id:"0-AM-PY",name:"Paraguay"});mortgagesConfig.countries.push({id:"0-AM-PE",name:"Perú"});mortgagesConfig.countries.push({id:"0-OC-PF",name:"Polinesia Francesa"});mortgagesConfig.countries.push({id:"0-EU-PL",name:"Polonia"});mortgagesConfig.countries.push({id:"0-EU-PT",name:"Portugal"});mortgagesConfig.countries.push({id:"0-AM-PR",name:"Puerto Rico"});mortgagesConfig.countries.push({id:"0-AS-QA",name:"Qatar"});mortgagesConfig.countries.push({id:"0-EU-GB",name:"Reino Unido"});mortgagesConfig.countries.push({id:"0-AF-CF",name:"República Centroafricana"});mortgagesConfig.countries.push({id:"0-EU-CZ",name:"República Checa"});mortgagesConfig.countries.push({id:"0-AM-DO",name:"República Dominicana"});mortgagesConfig.countries.push({id:"0-AF-RE",name:"Reunión"});mortgagesConfig.countries.push({id:"0-AF-RW",name:"Ruanda"});mortgagesConfig.countries.push({id:"0-EU-RO",name:"Rumanía"});mortgagesConfig.countries.push({id:"0-AS-RU",name:"Rusia"});mortgagesConfig.countries.push({id:"0-AF-EH",name:"Sahara Occidental"});mortgagesConfig.countries.push({id:"0-OC-WS",name:"Samoa"});mortgagesConfig.countries.push({id:"0-OC-AS",name:"Samoa Americana"});mortgagesConfig.countries.push({id:"0-AM-KN",name:"San Cristóbal y Nieves"});mortgagesConfig.countries.push({id:"0-EU-SM",name:"San Marino"});mortgagesConfig.countries.push({id:"0-AM-PM",name:"San Pedro y Miquelón"});mortgagesConfig.countries.push({id:"0-AM-VC",name:"San Vicente y las Granadinas"});mortgagesConfig.countries.push({id:"0-AM-LC",name:"Santa Lucía"});mortgagesConfig.countries.push({id:"0-AF-ST",name:"Santo Tomé y Príncipe"});mortgagesConfig.countries.push({id:"0-AF-SN",name:"Senegal"});mortgagesConfig.countries.push({id:"0-EU-RS",name:"Serbia"});mortgagesConfig.countries.push({id:"0-AF-SC",name:"Seychelles"});mortgagesConfig.countries.push({id:"0-AF-SL",name:"Sierra Leona"});mortgagesConfig.countries.push({id:"0-AS-SG",name:"Singapur"});mortgagesConfig.countries.push({id:"0-AS-SY",name:"Siria"});mortgagesConfig.countries.push({id:"0-AF-SO",name:"Somalia"});mortgagesConfig.countries.push({id:"0-AS-LK",name:"Sri Lanka"});mortgagesConfig.countries.push({id:"0-AF-ZA",name:"Sudáfrica"});mortgagesConfig.countries.push({id:"0-AF-SD",name:"Sudán"});mortgagesConfig.countries.push({id:"0-EU-SE",name:"Suecia"});mortgagesConfig.countries.push({id:"0-EU-CH",name:"Suiza"});mortgagesConfig.countries.push({id:"0-AM-SR",name:"Surinam"});mortgagesConfig.countries.push({id:"0-EU-SJ",name:"Svalbard y Jan Mayen"});mortgagesConfig.countries.push({id:"0-AS-TH",name:"Tailandia"});mortgagesConfig.countries.push({id:"0-AS-TW",name:"Taiwán"});mortgagesConfig.countries.push({id:"0-AF-TZ",name:"Tanzania"});mortgagesConfig.countries.push({id:"0-AS-TJ",name:"Tayikistán"});mortgagesConfig.countries.push({id:"0-AF-TG",name:"Togo"});mortgagesConfig.countries.push({id:"0-OC-TK",name:"Tokelau"});mortgagesConfig.countries.push({id:"0-OC-TO",name:"Tonga"});mortgagesConfig.countries.push({id:"0-AM-TT",name:"Trinidad y Tobago"});mortgagesConfig.countries.push({id:"0-AF-TN",name:"Túnez"});mortgagesConfig.countries.push({id:"0-AS-TM",name:"Turkmenistán"});mortgagesConfig.countries.push({id:"0-AS-TR",name:"Turquía"});mortgagesConfig.countries.push({id:"0-OC-TV",name:"Tuvalu"});mortgagesConfig.countries.push({id:"0-EU-UA",name:"Ucrania"});mortgagesConfig.countries.push({id:"0-AF-UG",name:"Uganda"});mortgagesConfig.countries.push({id:"0-AM-UY",name:"Uruguay"});mortgagesConfig.countries.push({id:"0-AS-UZ",name:"Uzbekistán"});mortgagesConfig.countries.push({id:"0-OC-VU",name:"Vanuatu"});mortgagesConfig.countries.push({id:"0-AM-VE",name:"Venezuela"});mortgagesConfig.countries.push({id:"0-AS-VN",name:"Vietnam"});mortgagesConfig.countries.push({id:"0-OC-WF",name:"Wallis y Futuna"});mortgagesConfig.countries.push({id:"0-AS-YE",name:"Yemen"});mortgagesConfig.countries.push({id:"0-AF-DJ",name:"Yibuti"});mortgagesConfig.countries.push({id:"0-AF-ZR",name:"Zaire"});mortgagesConfig.countries.push({id:"0-AF-ZM",name:"Zambia"});mortgagesConfig.countries.push({id:"0-AF-ZW",name:"Zimbabue"});
</script> <script type="text/microtemplate" id="lightbox-header-images-tmpl">
<span id="image-gallery-tag" class="main-title"></span> <span id="image-gallery-share-links"> <div> <a href="#" id="image-gallery-share-friend-link" data-xiti-page="detalle::conversiones::form_compartir_foto" data-xiti-shared-n2="true" class="icon-share"><span>Compartir</span></a> <a href="#" id="gallery-contact-btn" class="icon-mail" data-markup='detalle::photo::contact'><span>Contactar</span></a> </div> </span> <span id="image-gallery-pager"></span> <span class="lightbox-close icon-close" data-xiti-page></span>
</script> <script type="text/microtemplate" id="lightbox-header-map-tmpl">
<div class="address-tag"> <span id="map-tag" class="main-title">Mapa de la zona</span> <span id="address">Garaje en venta en Passatge de Simó, 10</span> <span class="map-links"> | <a href="#" class="active" data-xiti-page="detalle::streetview" data-xiti-shared-n2="true">Ver foto de la calle</a> <a href="#" class="d-none" data-xiti-page="detalle::mapa" data-xiti-shared-n2="true">Ver mapa</a> </span> </div> <span class="lightbox-close icon-close"></span> <div id="warning-address" class="contextual warning icon-feedbk-alert">El anunciante prefiere no mostrar la dirección exacta, pero más o menos por aquí...</div>
</script> <script type="text/javascript" defer src="https://st1.idealista.com/static/common/release/detail/detail.js?20201222-0907"></script> <script type="text/javascript" defer src="https://st1.idealista.com/static/react/gallery/dist/loadScripts.js?20201222-0907"></script> <script type="text/javascript" defer src="https://st1.idealista.com/static/react/homestaging/dist/loadScripts.js?20201222-0907"></script> <script type="text/javascript" defer src="https://st1.idealista.com/static/react/detailWithSuggestions/dist/loadScripts.js?20201222-0907"></script> <script type="text/microtemplate" id="login-box-title-fav">
<span class="icon-fav">
<span class="txt-bold">Guardar favorito</span>
</span>
</script> <script type="text/microtemplate" id="login-box-title-discard">
<span class="icon-delete">
<span class="txt-bold">Descartar</span>
</span>
</script> <script type="text/microtemplate" id="login-box-title-favfilter">
<span class="txt-bold">Ver tus favoritos en esta búsqueda</span>
</script> <script type="text/microtemplate" id="login-box-title-stats">
<span class="txt-bold">Ver número de visitas y contactos</span>
</script> <script type="text/microtemplate" id="login-box-content-text-fav">
<div class="info-txt save-ad-text">
<p>Puedes crear una lista de tus anuncios favoritos y te avisaremos si cambian de precio, añaden nuevas fotos, o si se elimina.</p>
<p>También podrás escribir notas personales que sólo verás tú, para recordar detalles que te interesen.</p>
</div>
</script> <script type="text/microtemplate" id="login-box-content-text-discard">
<div class="info-txt">
<p>Descarta los anuncios que no te interesan y dejarás de verlos cuando busques.</p>
<p>Si luego te arrepientes los puedes recuperar desde tus descartados.</p>
</div>
</script> <script type="text/microtemplate" id="login-box-content-text-favfilter">
<div class="info-txt">
<p>Consulta de forma rápida los favoritos que tienes en cada búsqueda y fíltralos por los criterios que quieras</p>
</div>
</script> <script type="text/microtemplate" id="login-box-content-text-stats">
<div class="info-txt">
<p>Consulta los datos de popularidad de los anuncios que te interesan: cuántas visitas y contactos han tenido, y cuántas personas lo tienen en sus favoritos</p>
</div>
</script> <script type="text/microtemplate" id="login-box-content-tmpl">
<div class="login-box-form modal-inner"> {{=contentExplanation}} <div id="loginlicker"> <label for="email"> <span class="username initial"> Tu email </span> <span class="username named" style="display:none;" data-username="Hola de nuevo :-)"> </span> <input id="email" class="email" type="email" name="email" autocomplete="email"/> <span class="email-feedback">Venga, escribe un email ;-)</span> </label> <p class="spinner-inline left"> Comprobando email </p> <div class="item-form pass-wrapper"> <label> <span class="username">Tu contraseña</span> <input type="password" class="password js-password-field" name="pwd" data-password-show="Mostrar" data-password-hide="Ocultar"/> <span class="password-feedback">Venga, escribe la contraseña ;-)</span> </label> </div> <p><a href="/recordar-password" class="forgotpassword">¿Has olvidado tu contraseña?</a></p> <label class="input-checkbox"> <input name="cookie" type="checkbox" checked="checked"/> <span><span>Recordar tus datos</span></span> </label> <label> <span class="msg-repeat"> Por favor, comprueba que tu email está bien escrito </span> <span class="repeat"></span> </label> <label> <span id="msg-accept-privacy-policy"> Tienes que aceptar la política de privacidad para poder continuar </span> </label> <label class="input-checkbox"> <input id="privacy-policy" name="privacy-policy" type="checkbox" data-markup-privacy/> <span><span>Aceptar la <a href="https://www.idealista.com/ayuda/articulos/politica-de-privacidad/" target="_blank">política de privacidad</a></span></span> </label> <label class="input-checkbox"> <input id="subscribe-mailing" name="subscribe-mailing" type="checkbox" data-markup-communications/> <span><span>Recibir información de inmuebles, noticias y otras comunicaciones promocionales desde idealista, idealista/data, idealista/hipotecas o Rentalia basadas en tu perfil.</span></span> </label> </div> </div>
</script> <script type="text/microtemplate" id="login-box-button-initial">
<div class="modal-inner">
<a href="#" class="btn action longer accept dummy">
¡Vamos!
</a>
</div>
</script> <script type="text/microtemplate" id="login-box-button-fav">
<div class="modal-inner">
<a href="#" class="btn action longer accept">
Guardar favorito
</a>
</div>
</script> <script type="text/microtemplate" id="login-box-button-discard">
<div class="modal-inner">
<a href="#" class="btn action longer accept">
Descartar anuncio
</a>
</div>
</script> <script type="text/microtemplate" id="login-box-button-favfilter">
<div class="modal-inner">
<a href="#" class="btn action longer accept">
Ver sólo favoritos
</a>
</div>
</script> <script type="text/microtemplate" id="login-box-button-stats">
<div class="modal-inner">
<a href="#" class="btn action longer accept">
Ver estadísticas
</a>
</div>
</script> <script type="text/microtemplate" id="login-box-feedback-generic-content">
<p class="feedback warning icon-feedbk-alert">Sólo falta que confirmes tu email</p>
<div class="modal-inner">
<p>Hemos enviado un correo a {{=email}}, no olvides revisar tu correo no deseado, por si acaso ;-)</p>
</div>
</script> <script type="text/microtemplate" id="login-box-feedback-generic-buttons">
<div class="modal-inner">
<a href="#" class="btn regular longer accept">
Entendido
</a>
</div>
</script> <script type="text/microtemplate" id="promo-app-title-tmpl">
<span class="txt-bold">Bájate nuestra app</span>
</script> <script type="text/microtemplate" id="promo-app-content-tmpl">
<img class="promoApp" src="">
<p>Recibe avisos inmediatos de tus búsquedas y favoritos en cualquier lugar con la app gratuita de idealista</p>
<div class="download"><a id="buttonDownloadApp" class="btn action txt-bold" data-markup='promo-app::download' href="http://m.onelink.me/d181dd66">Descargar ahora</a></div>
<div class="cancel"><a class="buttonCancelDownloadApp" id="buttonCancelDownloadApp" data-markup="promo-app::no-thanks">No, gracias</a></div>
</script> <script src="https://www.google.com/recaptcha/api.js?render=6LceRo8UAAAAAFbfO3vhtiQk66j-fJEkQichNAk2" async defer></script> <script>
var configTwoSteps = {
urls: {
defaultLogin: "/es/login.ajax",
defaultPreauthorized: "/es/login-preauthorized.ajax",
userStatus: "/check-identification-service-user-status.ajax",
emailVerifyUrl: "/ajax/alerts-ls-existence-and-active"
},
userStatus: "", // authorized, preauthorized, blocked, error
recaptchaToggle: false,
recaptcha2slToggle: true,
recaptcha2slPublicKey: "6LceRo8UAAAAAFbfO3vhtiQk66j-fJEkQichNAk2",
recaptcha2slActionName: "login2sl",
recaptchaNotifyIncidence: "notifyIncidence",
deviceInfoToggle: false
};
</script> <script>
var configSendIncidences = {
recaptchaPublicKey: "6LceRo8UAAAAAFbfO3vhtiQk66j-fJEkQichNAk2",
recaptchaAction: "notifyIncidence",
};
</script> <script>
var configRecaptchaSendContact = {
recaptchaPublicKey: "6LceRo8UAAAAAFbfO3vhtiQk66j-fJEkQichNAk2",
recaptchaAction: "sendContact",
};
</script> <script type="text/microtemplate" id="tsa-confirm-title">
Verificación de identidad
</script> <script type="text/microtemplate" id="tsa-confirm-send-again-pls">
<div class="modal-inner">
<p>¿No lo has recibido o no tienes el móvil contigo?</p>
<p class="icon-leads"><a href="#" id="sendAgain">Enviar nuevo código al móvil</a></p>
<p class="icon-phone">
Ponte en contacto con nuestro equipo llamando al
<a class="phone" href="tel://900 37 30 02">900 37 30 02</a>
</p>
<div class="item-form">
<label>
<span>Código de verificación</span>
<input type="text" id="code_2sa" name="code" data-validation="required" data-message-required="Venga, escribe el código ;-)" />
</label>
</div>
</div>
</script> <script type="text/microtemplate" id="tsa-confirm-content">
<p class="feedback success icon-feedbk-ok">Hemos enviado un código al {{=phone}}</p> <div class="modal-inner"> <p>Por tu seguridad te recomendamos que antes de continuar compruebes el estado de tu conexión. Para ello deberías ver este <span class="nobreak">icono <img src="https://st1.idealista.com/static/common/release/user-management/resources/img/locker.png"/></span> junto a la dirección web de idealista. </p> </div> <div class="modal-inner"> <div class="item-form"> <label> <span>Código de verificación</span> <input type="text" id="code_2sa" name="code" data-validation="required" data-message-required="Venga, escribe el código ;-)"/> </label> <a href="#" id="resendCode">¿No lo has recibido o no tienes el móvil contigo?</a> </div> </div>
</script> <script type="text/microtemplate" id="tsa-confirm-buttons">
<div class="modal-inner"> <a href="#" title="Confirmar y entrar" class="btn action accept confirm-btn">Confirmar y entrar</a> </div>
</script> <script type="text/microtemplate" id="tsa-confirm-wrong-code-feedback">
El código insertado no coincide con el que te hemos enviado.
</script> <script type="text/microtemplate" id="tsa-contactus-title">
Verificación de identidad
</script> <script type="text/microtemplate" id="tsa-contactus-insistent-feedback">
Para continuar llama al número que te mostramos y sigue nuestras indicaciones
</script> <script type="text/microtemplate" id="tsa-contactus-content">
<p class="feedback error icon-feedbk-ko">Parece que estamos teniendo algún problema en validar el código que intentas insertar</p> <div class="modal-inner"> <div class="contactus"> <p> Ponte en contacto con nuestro equipo llamando al <a class="phone" href="tel://902 50 80 00">902 50 80 00</a> y te proporcionaremos un nuevo código de verificación </p> </div> </div>
</script> <script type="text/microtemplate" id="tsa-contactus-buttons">
<div class="modal-inner"> <a href="#" title="Insertar nuevo código" class="btn regular accept">Insertar nuevo código</a> </div>
</script> <script type="text/microtemplate" id="tsa-contactus-insert-content">
<div class="modal-inner"> <p>Inserta el código que te hemos facilitado durante la llamada para acceder a tu cuenta</p> </div> <div class="modal-inner"> <div class="item-form"> <label> <span>Código de verificación</span> <input type="text" id="code_2sa" name="code" data-validation="required" data-message-required="Venga, escribe el código ;-)"/> </label> </div> </div>
</script> <script type="text/microtemplate" id="tsa-contactus-insert-buttons">
<div class="modal-inner"> <a href="#" title="Confirmar y entrar" class="btn action accept confirm-btn">Confirmar y entrar</a> </div>
</script> <script type="text/microtemplate" id="blockedemail-modal-title">
Cuenta bloqueada
</script> <script type="text/microtemplate" id="blockedemail-modal-content">
<p class="feedback warning icon-feedbk-alert"> Cuenta bloqueada de manera preventiva </p> <div class="modal-inner"> <p> <span class="info1">Hemos bloqueado temporalmente tu cuenta {{=email}} por motivos de seguridad.</span> </p> <p> <span class="info2">Llámanos al <strong>917 01 40 30</strong><br></span> <span class="info3">(de lunes a viernes de 9:00 a 21:00)<br></span> <span class="info4">Si tienes un anuncio publicado, llámanos desde el teléfono que tengas en el anuncio y te indicaremos los pasos a seguir.</span> </p> <p> <a class="icon-phone btn action btn-phone" href="tel://917014030">917 01 40 30</a> </p> </div>
</script> <script type="text/microtemplate" id="contact-feedback-tmpl">
<div class="contextual success icon-thumbup"> {{normalMessage}} {{userName}} ¡Genial! mensaje enviado a <b><span class='capitalized'>{{=userName}}</span></b> {{:userName}} ¡Genial! mensaje enviado {{/userName}} {{:normalMessage}} mensaje enviado a las inmobliarias {{/normalMessage}} </div> {{saveAlert}} <div> <p class="text-save-search"> Guarda tu búsqueda para recibir por email nuevos anuncios similares a este </p> <a href="#" class="btn action expand txt-bold txt-big save-search" title='Guardar búsqueda' data-summaryurl="/recibir-alertas-gratis-primer-resumen/25" data-saveurl="/savealert.ajax/20" data-saverelatedurl="/ajax/save-related-alert-popup.ajax/29" data-emailverifyurl="/ajax/alerts-ls-existence-and-active/18" data-forgotpasswordurl="/recordar-password" data-didntgetmailurl="/info/info-problema-email"> Guardar búsqueda </a> {{/saveAlert}} {{suggestAgencies}} <p> Estas inmobiliarias te pueden ayudar a buscar inmuebles similares </p> <ul> {{@suggestedAgencies}} <li> <input type="checkbox" id="suggest_inmo_{{=_key}}" data-inmo-id="{{=_val.commercialDataId}}" checked/> <label for="suggest_inmo_{{=_key}}"> {{=_val.commercialName}} </label> </li> {{/@suggestedAgencies}} </ul> <a href="#" class="btn regular contact-pro expand" title='Contactar agencias'> Contactar agencias </a> {{/suggestAgencies}} </div>
</script> <script type="text/microtemplate" id="unblock-feedback">
<div class="contact-feedback feedback success icon-thumbup">
<span>Ya puedes volver a contactar a {{=userName}}</span>
</div>
</script> <script type="text/microtemplate" id="unblock-error-feedback">
<div class="contact-error-feedback feedback error icon-feedbk-ko">
<span>algo no ha ido bien, por favor vuelve a intentarlo</span>
</div>
</script> <script type="text/microtemplate" id="contact-unblock-modal-title">
Anunciante bloqueado por ti
</script> <script type="text/microtemplate" id="contact-unblock-modal">
<div class="modal-inner">
<p class="contact-again-title">Para volver a contactar, debes desbloquearle antes.</p>
<div class ="unblock-container-info">
<div class="unblock-container-info-content">
<div class="container">
<div class="img-container">
<div class="avatar">
{{info.image}}
<img class="user-image" src="{{=info.image}}">
{{:info.image}}
<span class="icon-user"></span>
{{/info.image}}
<img class="icon-blocked-user" width="24" height="24" src="https://st1.idealista.com/static/common/img/icons/ic-blocked-on-avatar.svg">
</div>
</div>
<div class="info">
<p class="user-name">{{=info.userName}}</p>
<p>{{=info.address}} {{=info.price}}</p>
<p>Tu motivo fue:</p>
{{info.blockedInfo.offensiveMessages}}
<p>"Mensajes ofensivos"</p>
{{/info.blockedInfo.offensiveMessages}}
{{info.blockedInfo.seemsFraud}}
<p>"Parece fraude"</p>
{{/info.blockedInfo.seemsFraud}}
{{info.blockedInfo.otherReason}}
<p>"<span class="other-reason">{{=info.blockedInfo.text}}</span>"</p>
{{/info.blockedInfo.otherReason}}
<p class="blocking-date">Bloqueado desde el {{=info.blockedInfo.date}}</p>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn action">Desbloquear</button>
<a href="{{=info.url}}" class="link">Ver tu conversación con <span class='capitalized'>{{=info.userName}}</span></a>
</div>
</div>
</script> <script type="text/microtemplate" id="unblock-login-modal-title">
Entrar en idealista
</script> <script type="text/microtemplate" id="unblock-login-modal">
<div class="modal-inner">
<p>Para contactar con este anunciante con el email {{=email}}, debes entrar en la cuenta de idealista asociada a este correo.</p>
<div class="modal-footer">
<a class="btn action" href="/redirectToLogin">Entrar en idealista</a>
</div>
</div>
</script> <script type="text/microtemplate" id="counteroffer-tmpl" data-modal-title="Hacer una contraoferta" data-service-error='¡Vaya! No hemos podido enviar tu mensaje ¡Prueba otra vez!'>
<div class="modal-inner" contact-modal-type="counteroffer"> <form action="" data-service-error='¡Vaya! No hemos podido enviar tu mensaje ¡Prueba otra vez!' novalidate> {{showFeedback}} <div class="feedbacks-container"> {{=feedbackTmpl}} </div> {{/showFeedback}} {{showForm}} <ul> <li class="item-form"> Precio actual: 17.900 € </li> <li class="item-form"> <label for="counteroffer-how"> ¿Cuánto pagarías? </label> <input name="counteroffer-how" id="counteroffer-how" type="text" value="" class="half-input" data-validation="required" data-message-required='Indica cuanto estás dispuesto a pagar' autofocus> € <span data-error-here></span> </li> <li class="item-form contacted{{showContactTime}} show{{/showContactTime}}"> <span class="icon-mail">Has contactado </span> </li> <li class="item-form"> <label for="counteroffer-comments"> Más comentarios </label> <textarea name="counteroffer-comments" id="counteroffer-comments" type="text"></textarea> {{showSuggestedMessages}} <ul class="last-contact-messages"> {{suggestedMessages}} {{=suggestedMessages}} {{/suggestedMessages}} </ul> {{/showSuggestedMessages}} </li> <li class="item-form"> <label for="counteroffer-email"> Tu email </label> <input name="counteroffer-email" id="counteroffer-email" type="email" class="js-email-field" value="" autocomplete="email" data-validation="required email" data-message-email="Revisa el formato de tu email" data-message-required='Revisa el formato de tu email'> </li> <li class="item-form half-items clearfix"> <div> <label for="counteroffer-phone"> Tu teléfono </label> <input name="counteroffer-phone" id="counteroffer-phone" type="tel" class="js-phone-field" value="" data-validation="phone required" data-message-phone="Revisa tu teléfono" data-message-required='Revisa tu teléfono'> </div> <div> <label for="counteroffer-name"> Tu nombre </label> <input name="counteroffer-name" id="counteroffer-name" type="text" data-validation="name" value="" data-message-name="Esto no parece un nombre"> </div> </li> <li> <div class="captcha-miniFields d-none" data-service-prefix="counterOffer"> <div class="captcha"> <div class="captcha-img"> </div> </div> <div class="item-form half-items clearfix"> <label> <span>Escribe los números y letras que ves arriba</span> <div> <input type="text" class="_captcha" name="contact-captcha" autocapitalize="none" autocorrect="off"/> </div> </label> <p><a class="captcha-reload" href="#">¿No entiendes lo que pone?</a></p> </div> </div> </li> <div id="privacy-policy-detail" class="item-form privacy-policy-container"> <label class="input-checkbox" data-validation="checkbox" data-message-checkbox="Para contactar debes aceptar la política de privacidad" data-error-name="privacy-policy-checkbox"> <input type="checkbox" name="privacy-policy-checkbox"/> <span><span> Aceptar <a href="https://www.idealista.com/ayuda/articulos/politica-de-privacidad/" target="_blank">política de privacidad</a> </span></span> </label> </div> <div id="recommendations-detail" class="item-form recommendations-container"> <div> <label class="input-checkbox" data-error-name="recommendations-checkbox"> <input type="checkbox" name="recommendations-checkbox" data-markup-co-recommendations='detalle::conversiones::form_contraoferta::recomendations-'/> <span> <span> Recibir inmuebles de idealista similares a este </span> </span> </label> </div> </div> <input type="hidden" name="operationId" value="1"/> <input type="hidden" name="type" id="contact-type" value=""/> <li class="item-form"> <span class="service-error-container contextual full-width error icon-feedbk-ko d-none"></span> <div class="d-none"> <input type="text" value="" name="counteroffer-mail"> </div> <input type="hidden" name="adId" value="92174198"/> <input class="btn action txt-bold" value='Hacer una contraoferta' type="submit" id="counteroffer-send" data-xiti-page="contactar-mail-particular-v-detalle"> </li> </ul> {{/showForm}} </form> </div>
</script> <script type="text/microtemplate" id="saveAlertsPopupTitle">
<div class="modal-inner"> Antes de seguir, comprueba que estos criterios son correctos </div>
</script> <script type="text/microtemplate" id="saveAlertsPopupContent">
<div class="modal-inner"> <ul class="criterios"> {{@criterios}}<li>{{=_val}}</li>{{/@criterios}} </ul> <span class="advice"> {{avisoNormal}} Se publican de media <strong>{{=numAnuncios}} anuncios nuevos al día</strong> con estos criterios {{/avisoNormal}} {{avisoMinimo}} <span class="lowFrequency"><strong>¡Ojo! Te llegarán muy pocos avisos</strong></span><br/> {{minimoOne}} <span class="lowFrequency">Se publica de media menos de 1 anuncio nuevo al día con estos criterios</span> {{:minimoOne}} <span class="lowFrequency">Se publican de media menos de {{=numAnuncios}} anuncios nuevos al día con estos criterios</span> {{/minimoOne}} {{/avisoMinimo}} {{avisoMaximo}} <span class="highFrequency"><strong>¡Ojo! Te llegarán muchos avisos</strong></span><br/> <span class="highFrequency">Se publican de media más de {{=numAnuncios}} anuncios nuevos al día con estos criterios</span> {{/avisoMaximo}} </span> </div>
</script> <script type="text/microtemplate" id="saveAlertsPopupButtons">
<div class="modal-inner"> {{continuarSolo}} <a href="#" class="continuarlink accept">Vale está bien, continuar &raquo;</a> {{:continuarSolo}} <a href="#" class="btn regular accept">Sí son correctos, continuar</a> <a href="#" class="revisarCriterios">Revisar criterios</a> {{/continuarSolo}} </div>
</script> <form method="post" name="suggestForm" id="suggestForm" class="is-hidden" action="/seleccionar-tipo-alerta" data-popupURL="/recibir-alertas-gratis-primer-resumen"> <input name="mailSaveSearch" type="hidden" value=""/> <input type="hidden" name="savingAlertStartingPoint" value=""> <script type="text/microtemplate" class="title">
Guarda la búsqueda para no perderte los anuncios que te interesan
</script> <script type="text/microtemplate" class="content">
<p>Te mandamos por email los anuncios nuevos que se publiquen con tus criterios</p> <ul class="criterios"> {{@criterios}}<li>{{=_val}}</li>{{/@criterios}} </ul> <span class="advice"> Se publican de media <strong>{{=numAnuncios}} anuncios nuevos al día</strong> con estos criterios </span>
</script> <script type="text/microtemplate" class="buttons">
<a href="#" class="btn regular accept">Vale, guardar búsqueda</a> <a href="#" class="cancel">No, gracias</a>
</script> </form> <script type="text/microtemplate" id="share-module-tmpl" data-title-normal="Compartir" data-title-picture="Enviar esta foto a un amigo" data-title-virtualtour="Enviar Virtual Tour 360 a un amigo">
<div class="modal-inner"> <ul class="copy-url__social"> <li class="copy-url__social-whatsapp"> <a class="share-whatsapp-link icon-whatsapp-outline" data-markup="share-whatsapp" href="http://api.whatsapp.com/send?text=http://www.idealista.com/inmueble/92174198/" target="_blank"></a> </li> </ul> <h4 class="share-email-subtitle">Compartir por email</h4> <form action="" novalidate> <div class="loading-layer"> <div class="loading"></div> </div> {{showMessage}} <div class="feedback success icon-feedbk-ok"> {{picture.id}} Hemos enviado la foto a tus amigos {{:picture.id}} {{virtualtour.id}} Hemos enviado el Virtual Tour 360 a tus amigos {{:virtualtour.id}} Hemos enviado el anuncio a tus amigos {{/virtualtour.id}} {{/picture.id}} </div> {{/showMessage}} {{showFavAdded}} <p>También hemos guardado el anuncio como favorito para tu comodidad</p> {{/showFavAdded}} {{virtualtour.id}} <div class="picture-container virtualtour-container"> <img src="{{=virtualtour.id}}" alt="{{=virtualtour.id}}" class="pictureSend horizontal"/> </div> {{/virtualtour.id}} {{picture.id}} <div class="picture-container"> <img src="{{=picture.url}}" alt="{{=picture.url}}" class="pictureSend {{=picture.orientation}}"/> </div> <input type="hidden" name="share-to-friend-to-picture_id" value="{{=picture.id}}"/> {{/picture.id}} {{virtualtour.id}} <input type="hidden" name="share-to-friend-to-virtualtour_id" value="{{=virtualtour.id}}"/> {{/virtualtour.id}} {{showForm}} <ul> <li class="item-form"> <label for="share-to-friend-to">Email de tus amigos</label> <input name="share-to-friend-to" id="share-to-friend-to" type="email" value="" data-validation="required multiple_emails" data-message-required="Dinos a quién se lo enviamos ;-)" data-message-multiple_emails="Revisa el formato de los emails de tus amigos" autofocus> <span class="helpText">Si son varios sepáralos con una coma (,)</span> </li> <li class="item-form"> <label for="share-to-friend-from">Tu email</label> <input name="contact-email" id="share-to-friend-from" value="" type="email" autocomplete="email" data-validation="required email" data-message-required="Indica tu email" data-message-email="Revisa el formato de tu email"> <span class="helpText"> {{picture.id}} Quien recibe la foto lo verá {{:picture.id}} {{virtualtour.id}} Quien recibe el Virtual Tour 360 lo verá {{:virtualtour.id}} Quien recibe el anuncio lo verá {{/virtualtour.id}} {{/picture.id}} </span> </li> <li class="item-form"> <label for="share-to-friend-message">Tu mensaje</label> <textarea rows="2" name="contact-message" maxlength="1000" id="share-to-friend-message"></textarea> </li> <li> <input class="btn action txt-bold txt-big" value='Enviar' type="submit" id="share-friend-send"/> </li> </ul> <div class="copy-url"> <h4 class="share-email-subtitle">Copiar URL</h4> <div class="copy-url__box"> <input class="copy-url__box-text" type="text" readonly="readonly" value="www.idealista.com/92174198"/> <span class="icon-copy clipboard-share" data-markup="share-copy-link" data-txt-copied="Copiado">copiar</span> <span class="copy-url__box-feedback contextual success icon-feedbk-ok">copiado al portapapeles</span> </div> </div> <input type="hidden" id="adId" name="share-to-friend-adId" value="92174198"/> <input type="hidden" id="cookieEnabled" name="cookieEnabled" value="true"/> <div class="d-none"> <input type="text" value="" name="contact-mail"> </div> {{:showForm}} {{picture.id}} <p><a href="" data-xiti-shared-n2="true" data-xiti-page="detalle::conversiones::form_compartir_foto" title="Enviar esta foto a más gente" class="anotherTime">Enviar esta foto a más gente</a></<p> {{:picture.id}} {{virtualtour.id}} <p><a href="" data-xiti-shared-n2="true" data-xiti-page="detalle::conversiones::form_compartir_foto" title="Enviar este Virtual Tour 360 a más gente" class="anotherTime">Enviar este Virtual Tour 360 a más gente</a></p> {{:virtualtour.id}} <p><a href="" data-xiti-shared-n2="true" data-xiti-page="detalle::conversiones::form_compartir" data-markup="detalle::compartir::email" title="Envía este anuncio a más gente" class="anotherTime">Envía este anuncio a más gente</a></p> {{/virtualtour.id}} {{/picture.id}} <p> <a href="#" class="btn action longer cancel">Cerrar</a> </p> {{/showForm}} </form> </div>
</script> <script type="text/microtemplate" id="notify-incidence-step1">
<div class="modal-inner"> <form action="" method="post" id="ferror" name="ferror"> <div class="item-form help-us-modal"> <p> Anuncio:Garaje en venta en Passatge de Simó, 10 </p> <p class="icon-feedbk-info"> Esta información llega al equipo de calidad de idealista y al anunciante, pero él no conocerá tu identidad </p> <ul data-validation="radio" data-message-radio="Por favor selecciona una de las opciones que mejor describa el tipo de error que has visto"> <li> <label class="input-radio"> <input type="radio" id="iTipoComentario7" name="incidenceTypeId" value="7"/> <span><span>La dirección está mal</span></span> </label> </li> <li> <label class="input-radio"> <input type="radio" id="iTipoComentario4" name="incidenceTypeId" value="4"/> <span><span>El anunciante no es un particular</span></span> </label> </li> <li> <label class="input-radio"> <input type="radio" id="iTipoComentario6" name="incidenceTypeId" value="6"/> <span><span>El precio no es correcto</span></span> </label> </li> <li> <label class="input-radio"> <input type="radio" id="iTipoComentario11" name="incidenceTypeId" value="11"/> <span><span>Es una posible estafa</span></span> </label> </li> <li> <label class="input-radio"> <input type="radio" id="iTipoComentario13" name="incidenceTypeId" value="13"/> <span><span>Comentarios discriminatorios y/u ofensivos</span></span> </label> </li> <li> <label class="input-radio"> <input type="radio" id="iTipoComentario1" name="incidenceTypeId" value="1"/> <span><span>Error en las fotos</span></span> </label> </li> <li> <label class="input-radio"> <input type="radio" id="iTipoComentario8" name="incidenceTypeId" value="8"/> <span><span>El teléfono es erróneo o no lo cogen</span></span> </label> </li> <li> <label class="input-radio"> <input type="radio" id="iTipoComentario9" name="incidenceTypeId" value="9"/> <span><span>El email es erróneo o el anunciante no responde</span></span> </label> </li> <li> <label class="input-radio"> <input type="radio" id="iTipoComentario3" name="incidenceTypeId" value="3"/> <span><span>Ya está vendido</span></span> </label> </li> <li> <label class="input-radio"> <input type="radio" id="iTipoComentario5" name="incidenceTypeId" value="5"/> <span><span>Otro motivo</span></span> </label> </li> </ul> </div> <div class="item-form"> <label> <span>Descripción completa del error (opcional)</span> <textarea name="comments" maxlength="1000" id="ferror-text" placeholder="Este texto también le llegará al anunciante" data-message-required="Por favor, describe el error o errores que has visto"></textarea> <span id="comment" class="help-text block v-hidden">Este texto también le llegará al anunciante</span> </label> </div> <a href="#" class="btn action" id="submitBtn">Enviar error</a> </form> </div>
</script> <script type="text/microtemplate" id="notify-incidence-step2">
<div class="modal-inner modal-improve-feedback"> <form action="" id="ferror2" method="post"> <input type="hidden" name="incidenceId" id="incidenceId" value=""/> <div class="feedback success icon-feedbk-ok" id="feedback-success-div"> <p> Gracias por ayudarnos a mejorar idealista :-) <br> Te avisaremos cuando esté aclarado </p> </div> <div class="item-form"> <h3> ¿Quieres que te avisemos cuando se solucione? </h3> <p> Indícanos tus datos (no los recibirá el anunciante) </p> </div> <div class="item-form-and-half"> <label for="incidence-user-name"> Tu nombre y apellidos </label> <input name="name" id="incidence-user-name" value="" type="text" data-validation="required" data-message-required="Requerido"> </div> <div class="item-form-and-half"> <label for="incidence-user-email"> Tu email </label> <input name="mail" id="incidence-user-email" value="" type="email" autocomplete="email" data-validation="required email" data-message-required="Requerido" data-message-email="No es un mail válido"> </div> <div class="item-form privacy-policy-container"> <label class="input-checkbox" data-validation="checkbox" data-message-checkbox="Para contactar debes aceptar la política de privacidad" data-error-name="privacy-policy-checkbox"> <input type="checkbox" name="privacy-policy-checkbox"/> <span> <span> Aceptar <a href="https://www.idealista.com/ayuda/articulos/politica-de-privacidad/" target="_blank">política de privacidad</a> </span> </span> </label> </div> <a href="#" class="btn action" id="submitBtn"> Enviar </a> <a href="#" id="cancelBtn"> No, gracias </a> </form> </div>
</script> <script type="text/microtemplate" id="notify-incidence-step3">
<div class="modal-inner incidence-feedback"> <div class="help-us-modal feedback success icon-feedbk-ok" id="user-added-success-div"> <p> Gracias por ayudarnos a mejorar idealista :-) <br/> Te avisaremos cuando esté aclarado </p> </div> <a href="#" class="btn action" id="closeBtn"> Cerrar </a> </div>
</script> <script type="text/microtemplate" id="contact-mortgages-tmpl">
<div class="modal-inner"> <form id="send-contact-form" action="/enviar-contacto-hipoteca" method="post" novalidate> <input type="hidden" name="adId" value="92174198"> <input type="hidden" name="monthlyRate" value="{{=calculated.monthlyRate}}"> <input type="hidden" name="interestRate" value="{{=calculated.taxes}}"> <input type="hidden" name="years" value="{{=calculated.years}}"> <input type="hidden" name="mortgageFormType" value="0"> <input type="hidden" name="locationId" value="0-EU-ES-08"> <input type="hidden" name="locationName" value="La Sagrada Família"> <input type="hidden" name="propertyTypeId" value="1"> <input type="hidden" name="options" value="simulation"> <input type="hidden" name="simulationType" value="{{=calculated.simulationType}}"> <input type="hidden" name="origin" value="{{=calculated.simulationType}}"> <input type="hidden" name="mortgageSource" value=""> <input type="hidden" name="mortgageSourceDesc" value=""> <input type="hidden" name="locale" value="es"> <div class="hipotecas-logo"> <a target="_blank" href="/hipotecas/"> <span class="icon-idealista-icon"></span><span class="icon-hipotecas"></span> </a> </div> <div class="feedback-container d-none"> <div class="feedback success icon-feedbk-ok"> Tus datos han sido enviados <span class="info"> Si no quieres esperar llámanos al <span class="phoneDesktop">912 183 169</span> <a href="tel:912183169" class="phoneMobile">912 183 169</a> (lunes a viernes de 09h00 a 20h00) </span> </div> <div> <span>En breve nos pondremos en contacto contigo para analizar tu caso.</span> <span>Servicio gratuito y sin compromiso.</span> </div> <h4>Descubre las mejores hipotecas seleccionadas por idealista:</h4> <a id="comparator-link" href="#">Ver ofertas de hipotecas personalizadas</a> <a href="#" class="btn regular closeBtn cancel">Cerrar</a> </div> <div class="form-container"> <div id="contact-step1"> <header> <span class="txt-soft"> Nos peleamos con los bancos para conseguirte la mejor hipoteca. </span> <span class="txt-soft"> Servicio gratuito y sin compromiso </span> </header> <p class="user-information txt-medium txt-bold">Información sobre el inmueble</p> <div class="item-form half-items clearfix"> <div> <label><span>Tipo de compra</span> <select id="buyingTypeId" name="buyingTypeId" data-validation="select" data-message-select="Necesitamos conocer este dato"> <option value="">Seleccionar</option> <option value="5">Vivienda habitual</option> <option value="6">Segunda vivienda</option> <option value="10">No residente</option> <option value="13">Otros</option> </select> </label> </div> <div> <label><span>¿Cuándo vas a comprar?</span> <select id="buyingWhen" name="buyingWhen" data-validation="select" data-message-select="Necesitamos conocer este dato"> <option value="">Seleccionar</option> <option value="1">Ya he hecho la reserva </option> <option value="2">Quiero hacer una oferta</option> <option value="3">Estoy buscando vivienda</option> <option value="4">Solo quiero asesoramiento</option> </select> </label> </div> </div> <div class="item-form half-items clearfix"> <div> <label> <span>Precio del inmueble</span> <span class="desc-container"> <input type="tel" name="buyingPrice" data-validation="required number" maxlength="13" value="17900" data-message-required="Necesitamos conocer este dato" data-message-number="Debe ser un número"/> <span class="desc">Euros</span> </span> </label> </div> <div> <label> <span>Ahorro aportado</span> <span class="desc-container"> {{calculated.savings}} <input type="tel" name="savings" data-validation="required number" maxlength="13" value="{{=calculated.savings}}" data-message-required="Necesitamos conocer este dato" data-message-number="Debe ser un número"/> {{:calculated.savings}} <input type="tel" name="savings" data-validation="required number" maxlength="13" value="" data-message-required="Necesitamos conocer este dato" data-message-number="Debe ser un número"/> {{/calculated.savings}} <span class="desc">Euros</span> </span> </label> </div> </div> <div class="item-form"> <button id="next-to-step2-btn" type="button" class="btn action expand">Continuar</button> </div> </div> <div id="intermediate-step"> <header> <p class="txt-bold"> ¿Qué quieres hacer? </p> </header> <div class="mortgages-user-options"> <ul> <li class="user-options-list"> <label class="input-radio"> <input name="userOption" type="radio" data-value="2" checked> <span> <span class="txt-medium step-title input-title">Comparar ofertas de bancos</span> <span class="txt-soft input-info">Encuentra ofertas entre más de 15 bancos que se ajustan a tu perfil</span> </span> </label> </li> <li class="user-options-list"> <label class="input-radio"> <input name="userOption" type="radio" data-value="1"> <span> <span class="txt-medium step-title input-title">Encontrar la mejor hipoteca</span> <span class="txt-soft input-info">Analizamos tu caso y buscamos la hipoteca que mejor se adapta a ti</span> </span> </label> </li> <li class="user-options-list"> <label class="input-radio"> <input name="userOption" type="radio" data-value="3"> <span> <span class="txt-medium step-title input-title">Información sobre hipotecas</span> <span class="txt-soft input-info">Toda la información sobre hipotecas para resolver tus dudas y necesidades</span> </span> </label> </li> </ul> </div> <div class="item-form"> <button id="next-to-chosen-path" type="button" class="btn action expand">Continuar</button> </div> </div> <div id="no-valid-step1"> </div> <div id="contact-step2"> <header> <p> Nos peleamos con los bancos para conseguirte la mejor hipoteca. </p> <p> Servicio gratuito y sin compromiso </p> </header> <p class="user-information txt-bold">Información sobre ti</p> <div id="non-resident-buying" class="item-form half-items clearfix" style="display: none"> <div> <label><span>País de residencia</span> <select name="countryId" data-validation="select" data-message-select="Necesitamos conocer este dato"> <option value="">Seleccionar</option> <option value="0-AS-AF"> Afganistán </option> <option value="0-EU-AL"> Albania </option> <option value="0-EU-DE"> Alemania </option> <option value="0-EU-AD"> Andorra </option> <option value="0-AF-AO"> Angola </option> <option value="0-AM-AI"> Anguila </option> <option value="0-AM-AG"> Antigua y Barbuda </option> <option value="0-AM-AN"> Antillas Neerlandesas </option> <option value="0-AS-SA"> Arabia Saudí </option> <option value="0-AF-DZ"> Argelia </option> <option value="0-AM-AR"> Argentina </option> <option value="0-AS-AM"> Armenia </option> <option value="0-AM-AW"> Aruba </option> <option value="0-OC-AU"> Australia </option> <option value="0-EU-AT"> Austria </option> <option value="0-AS-AZ"> Azerbaiján </option> <option value="0-AM-BS"> Bahamas </option> <option value="0-AS-BD"> Bangladés </option> <option value="0-AM-BB"> Barbados </option> <option value="0-AS-BH"> Baréin </option> <option value="0-EU-BE"> Bélgica </option> <option value="0-AM-BZ"> Belice </option> <option value="0-AF-BJ"> Benín </option> <option value="0-AM-BM"> Bermudas </option> <option value="0-EU-BY"> Bielorrusia </option> <option value="0-AM-BO"> Bolivia </option> <option value="0-EU-BA"> Bosnia y Herzegovina </option> <option value="0-AF-BW"> Botsuana </option> <option value="0-AM-BR"> Brasil </option> <option value="0-AS-BN"> Brunéi </option> <option value="0-EU-BG"> Bulgaria </option> <option value="0-AF-BF"> Burkina Faso </option> <option value="0-AF-BI"> Burundi </option> <option value="0-AS-BT"> Bután </option> <option value="0-AF-CV"> Cabo Verde </option> <option value="0-AS-KH"> Camboya </option> <option value="0-AF-CM"> Camerún </option> <option value="0-AM-CA"> Canadá </option> <option value="0-AF-TD"> Chad </option> <option value="0-AM-CL"> Chile </option> <option value="0-AS-CN"> China </option> <option value="0-AS-CY"> Chipre </option> <option value="0-EU-VA"> Ciudad del Vaticano </option> <option value="0-AM-CO"> Colombia </option> <option value="0-AF-KM"> Comoras </option> <option value="0-AF-CG"> Congo </option> <option value="0-AS-KP"> Corea del Norte </option> <option value="0-AS-KR"> Corea del Sur </option> <option value="0-AM-CR"> Costa Rica </option> <option value="0-AF-CI"> Costa de Marfil </option> <option value="0-EU-HR"> Croacia </option> <option value="0-AM-CU"> Cuba </option> <option value="0-EU-DK"> Dinamarca </option> <option value="0-AM-DM"> Dominica </option> <option value="0-AM-EC"> Ecuador </option> <option value="0-AF-EG"> Egipto </option> <option value="0-AM-SV"> El Salvador </option> <option value="0-AS-AE"> Emiratos Árabes Unidos </option> <option value="0-AF-ER"> Eritrea </option> <option value="0-EU-SK"> Eslovaquia </option> <option value="0-EU-SI"> Eslovenia </option> <option value="0-EU-ES"> España </option> <option value="0-AM-US"> Estados Unidos </option> <option value="0-EU-EE"> Estonia </option> <option value="0-AF-SZ"> Esuatini </option> <option value="0-AF-ET"> Etiopía </option> <option value="0-AS-PH"> Filipinas </option> <option value="0-EU-FI"> Finlandia </option> <option value="0-OC-FJ"> Fiyi </option> <option value="0-EU-FR"> Francia </option> <option value="0-AF-GA"> Gabón </option> <option value="0-AF-GM"> Gambia </option> <option value="0-AS-GE"> Georgia </option> <option value="0-AF-GH"> Ghana </option> <option value="0-EU-GI"> Gibraltar </option> <option value="0-AM-GD"> Granada </option> <option value="0-EU-GR"> Grecia </option> <option value="0-AM-GL"> Groenlandia </option> <option value="0-AM-GP"> Guadalupe </option> <option value="0-OC-GU"> Guam </option> <option value="0-AM-GT"> Guatemala </option> <option value="0-AM-GF"> Guayana Francesa </option> <option value="0-EU-GG"> Guernesey </option> <option value="0-AF-GN"> Guinea </option> <option value="0-AF-GQ"> Guinea Ecuatorial </option> <option value="0-AF-GW"> Guinea-Bisáu </option> <option value="0-AM-GY"> Guyana </option> <option value="0-AM-HT"> Haití </option> <option value="0-AM-HN"> Honduras </option> <option value="0-EU-HU"> Hungría </option> <option value="0-AS-IN"> India </option> <option value="0-AS-ID"> Indonesia </option> <option value="0-AS-IQ"> Irak </option> <option value="0-AS-IR"> Irán </option> <option value="0-EU-IE"> Irlanda </option> <option value="0-AS-CX"> Isla Christmas </option> <option value="0-OC-NF"> Isla Norfolk </option> <option value="0-EU-IM"> Isla de Man </option> <option value="0-EU-IS"> Islandia </option> <option value="0-AM-KY"> Islas Caimán </option> <option value="0-AS-CC"> Islas Cocos </option> <option value="0-OC-CK"> Islas Cook </option> <option value="0-EU-FO"> Islas Feroe </option> <option value="0-AM-FK"> Islas Malvinas </option> <option value="0-OC-MP"> Islas Marianas del Norte </option> <option value="0-OC-MH"> Islas Marshall </option> <option value="0-OC-PN"> Islas Pitcairn </option> <option value="0-OC-SB"> Islas Salomón </option> <option value="0-AM-TC"> Islas Turcas y Caicos </option> <option value="0-AM-VI"> Islas Vírgenes </option> <option value="0-AM-VG"> Islas Vírgenes Británicas </option> <option value="0-AS-IL"> Israel </option> <option value="0-EU-IT"> Italia </option> <option value="0-AM-JM"> Jamaica </option> <option value="0-AS-JP"> Japón </option> <option value="0-EU-JE"> Jersey </option> <option value="0-AS-JO"> Jordania </option> <option value="0-AS-KZ"> Kazajistán </option> <option value="0-AF-KE"> Kenia </option> <option value="0-AS-KG"> Kirguistán </option> <option value="0-OC-KI"> Kiribati </option> <option value="0-AS-KW"> Kuwait </option> <option value="0-AS-LA"> Laos </option> <option value="0-AF-LS"> Lesoto </option> <option value="0-EU-LV"> Letonia </option> <option value="0-AS-LB"> Líbano </option> <option value="0-AF-LR"> Liberia </option> <option value="0-AF-LY"> Libia </option> <option value="0-EU-LI"> Liechtenstein </option> <option value="0-EU-LT"> Lituania </option> <option value="0-EU-LU"> Luxemburgo </option> <option value="0-EU-MK"> Macedonia del Norte </option> <option value="0-AF-MG"> Madagascar </option> <option value="0-AS-MY"> Malasia </option> <option value="0-AF-MW"> Malaui </option> <option value="0-AS-MV"> Maldivas </option> <option value="0-AF-ML"> Mali </option> <option value="0-EU-MT"> Malta </option> <option value="0-AF-MA"> Marruecos </option> <option value="0-AM-MQ"> Martinica </option> <option value="0-AF-MU"> Mauricio </option> <option value="0-AF-MR"> Mauritania </option> <option value="0-AF-YT"> Mayotte </option> <option value="0-AM-MX"> México </option> <option value="0-OC-FM"> Micronesia </option> <option value="0-EU-MD"> Moldavia </option> <option value="0-EU-MC"> Mónaco </option> <option value="0-AS-MN"> Mongolia </option> <option value="0-EU-ME"> Montenegro </option> <option value="0-AM-MS"> Montserrat </option> <option value="0-AF-MZ"> Mozambique </option> <option value="0-AS-MM"> Myanmar/Birmania </option> <option value="0-AF-NA"> Namibia </option> <option value="0-OC-NR"> Nauru </option> <option value="0-AS-NP"> Nepal </option> <option value="0-AM-NI"> Nicaragua </option> <option value="0-AF-NE"> Níger </option> <option value="0-AF-NG"> Nigeria </option> <option value="0-OC-NU"> Niue </option> <option value="0-EU-NO"> Noruega </option> <option value="0-OC-NC"> Nueva Caledonia </option> <option value="0-OC-NZ"> Nueva Zelanda </option> <option value="0-AS-OM"> Omán </option> <option value="0-EU-NL"> Países Bajos </option> <option value="0-AS-PK"> Pakistán </option> <option value="0-OC-PW"> Palaos </option> <option value="0-AM-PA"> Panamá </option> <option value="0-OC-PG"> Papúa Nueva Guinea </option> <option value="0-AM-PY"> Paraguay </option> <option value="0-AM-PE"> Perú </option> <option value="0-OC-PF"> Polinesia Francesa </option> <option value="0-EU-PL"> Polonia </option> <option value="0-EU-PT"> Portugal </option> <option value="0-AM-PR"> Puerto Rico </option> <option value="0-AS-QA"> Qatar </option> <option value="0-EU-GB"> Reino Unido </option> <option value="0-AF-CF"> República Centroafricana </option> <option value="0-EU-CZ"> República Checa </option> <option value="0-AM-DO"> República Dominicana </option> <option value="0-AF-RE"> Reunión </option> <option value="0-AF-RW"> Ruanda </option> <option value="0-EU-RO"> Rumanía </option> <option value="0-AS-RU"> Rusia </option> <option value="0-AF-EH"> Sahara Occidental </option> <option value="0-OC-WS"> Samoa </option> <option value="0-OC-AS"> Samoa Americana </option> <option value="0-AM-KN"> San Cristóbal y Nieves </option> <option value="0-EU-SM"> San Marino </option> <option value="0-AM-PM"> San Pedro y Miquelón </option> <option value="0-AM-VC"> San Vicente y las Granadinas </option> <option value="0-AM-LC"> Santa Lucía </option> <option value="0-AF-ST"> Santo Tomé y Príncipe </option> <option value="0-AF-SN"> Senegal </option> <option value="0-EU-RS"> Serbia </option> <option value="0-AF-SC"> Seychelles </option> <option value="0-AF-SL"> Sierra Leona </option> <option value="0-AS-SG"> Singapur </option> <option value="0-AS-SY"> Siria </option> <option value="0-AF-SO"> Somalia </option> <option value="0-AS-LK"> Sri Lanka </option> <option value="0-AF-ZA"> Sudáfrica </option> <option value="0-AF-SD"> Sudán </option> <option value="0-EU-SE"> Suecia </option> <option value="0-EU-CH"> Suiza </option> <option value="0-AM-SR"> Surinam </option> <option value="0-EU-SJ"> Svalbard y Jan Mayen </option> <option value="0-AS-TH"> Tailandia </option> <option value="0-AS-TW"> Taiwán </option> <option value="0-AF-TZ"> Tanzania </option> <option value="0-AS-TJ"> Tayikistán </option> <option value="0-AF-TG"> Togo </option> <option value="0-OC-TK"> Tokelau </option> <option value="0-OC-TO"> Tonga </option> <option value="0-AM-TT"> Trinidad y Tobago </option> <option value="0-AF-TN"> Túnez </option> <option value="0-AS-TM"> Turkmenistán </option> <option value="0-AS-TR"> Turquía </option> <option value="0-OC-TV"> Tuvalu </option> <option value="0-EU-UA"> Ucrania </option> <option value="0-AF-UG"> Uganda </option> <option value="0-AM-UY"> Uruguay </option> <option value="0-AS-UZ"> Uzbekistán </option> <option value="0-OC-VU"> Vanuatu </option> <option value="0-AM-VE"> Venezuela </option> <option value="0-AS-VN"> Vietnam </option> <option value="0-OC-WF"> Wallis y Futuna </option> <option value="0-AS-YE"> Yemen </option> <option value="0-AF-DJ"> Yibuti </option> <option value="0-AF-ZR"> Zaire </option> <option value="0-AF-ZM"> Zambia </option> <option value="0-AF-ZW"> Zimbabue </option> </select> </label> </div> </div> <div id="second-home-buying" class="item-form clearfix" style="display: none"> <span>¿Dónde vives?</span> <ul class="mortgages__residence--inputs list-horizontal clearfix" data-validation="radio"> <li> <label class="input-radio"> <input type="radio" name="secondHomeType" value="1" id="resident" checked="checked"/> <span> <span>España</span> </span> </label> </li> <li> <label class="input-radio"> <input type="radio" name="secondHomeType" value="2" id="non-resident"/> <span> <span>Extranjero</span> </span> </label> </li> </ul> <div id="residence-province" class="mortgages__residence--select item-form half-items clearfix"> <div> <label> <select id="province" name="secondHomeLocationId" data-validation="select" data-message-select="Necesitamos conocer este dato"> <option value="">Selecciona tu provincia</option> <option value="0-EU-ES-15"> A Coruña </option> <option value="0-EU-ES-01"> Álava </option> <option value="0-EU-ES-02"> Albacete </option> <option value="0-EU-ES-03"> Alicante </option> <option value="0-EU-ES-04"> Almería </option> <option value="0-EU-ES-53"> Andorra </option> <option value="0-EU-ES-33"> Asturias </option> <option value="0-EU-ES-05"> Ávila </option> <option value="0-EU-ES-06"> Badajoz </option> <option value="0-EU-ES-07"> Balears (Illes) </option> <option value="0-EU-ES-08"> Barcelona </option> <option value="0-EU-ES-09"> Burgos </option> <option value="0-EU-ES-10"> Cáceres </option> <option value="0-EU-ES-11"> Cádiz </option> <option value="0-EU-ES-39"> Cantabria </option> <option value="0-EU-ES-12"> Castellón </option> <option value="0-EU-ES-55"> Cerdanya Francesa </option> <option value="0-EU-ES-51"> Ceuta </option> <option value="0-EU-ES-13"> Ciudad Real </option> <option value="0-EU-ES-14"> Córdoba </option> <option value="0-EU-ES-16"> Cuenca </option> <option value="0-EU-ES-56"> Gibraltar </option> <option value="0-EU-ES-17"> Girona </option> <option value="0-EU-ES-18"> Granada </option> <option value="0-EU-ES-19"> Guadalajara </option> <option value="0-EU-ES-20"> Guipúzcoa </option> <option value="0-EU-ES-21"> Huelva </option> <option value="0-EU-ES-22"> Huesca </option> <option value="0-EU-ES-23"> Jaén </option> <option value="0-EU-ES-26"> La Rioja </option> <option value="0-EU-ES-35"> Las Palmas </option> <option value="0-EU-ES-24"> León </option> <option value="0-EU-ES-25"> Lleida </option> <option value="0-EU-ES-27"> Lugo </option> <option value="0-EU-ES-28"> Madrid </option> <option value="0-EU-ES-29"> Málaga </option> <option value="0-EU-ES-52"> Melilla </option> <option value="0-EU-ES-30"> Murcia </option> <option value="0-EU-ES-31"> Navarra </option> <option value="0-EU-ES-32"> Ourense </option> <option value="0-EU-ES-54"> País Vasco Francés </option> <option value="0-EU-ES-34"> Palencia </option> <option value="0-EU-ES-36"> Pontevedra </option> <option value="0-EU-ES-37"> Salamanca </option> <option value="0-EU-ES-38"> Santa Cruz de Tenerife </option> <option value="0-EU-ES-40"> Segovia </option> <option value="0-EU-ES-41"> Sevilla </option> <option value="0-EU-ES-42"> Soria </option> <option value="0-EU-ES-43"> Tarragona </option> <option value="0-EU-ES-44"> Teruel </option> <option value="0-EU-ES-45"> Toledo </option> <option value="0-EU-ES-46"> València </option> <option value="0-EU-ES-47"> Valladolid </option> <option value="0-EU-ES-48"> Vizcaya </option> <option value="0-EU-ES-49"> Zamora </option> <option value="0-EU-ES-50"> Zaragoza </option> </select> </label> </div> </div> <div id="residence-country" class="mortgages__residence--select item-form half-items clearfix"> <div> <label> <select name="secondHomeCountryId" data-validation="select" data-message-select="Necesitamos conocer este dato"> <option value="">Selecciona tu país</option> <option value="0-AS-AF"> Afganistán </option> <option value="0-EU-AL"> Albania </option> <option value="0-EU-DE"> Alemania </option> <option value="0-EU-AD"> Andorra </option> <option value="0-AF-AO"> Angola </option> <option value="0-AM-AI"> Anguila </option> <option value="0-AM-AG"> Antigua y Barbuda </option> <option value="0-AM-AN"> Antillas Neerlandesas </option> <option value="0-AS-SA"> Arabia Saudí </option> <option value="0-AF-DZ"> Argelia </option> <option value="0-AM-AR"> Argentina </option> <option value="0-AS-AM"> Armenia </option> <option value="0-AM-AW"> Aruba </option> <option value="0-OC-AU"> Australia </option> <option value="0-EU-AT"> Austria </option> <option value="0-AS-AZ"> Azerbaiján </option> <option value="0-AM-BS"> Bahamas </option> <option value="0-AS-BD"> Bangladés </option> <option value="0-AM-BB"> Barbados </option> <option value="0-AS-BH"> Baréin </option> <option value="0-EU-BE"> Bélgica </option> <option value="0-AM-BZ"> Belice </option> <option value="0-AF-BJ"> Benín </option> <option value="0-AM-BM"> Bermudas </option> <option value="0-EU-BY"> Bielorrusia </option> <option value="0-AM-BO"> Bolivia </option> <option value="0-EU-BA"> Bosnia y Herzegovina </option> <option value="0-AF-BW"> Botsuana </option> <option value="0-AM-BR"> Brasil </option> <option value="0-AS-BN"> Brunéi </option> <option value="0-EU-BG"> Bulgaria </option> <option value="0-AF-BF"> Burkina Faso </option> <option value="0-AF-BI"> Burundi </option> <option value="0-AS-BT"> Bután </option> <option value="0-AF-CV"> Cabo Verde </option> <option value="0-AS-KH"> Camboya </option> <option value="0-AF-CM"> Camerún </option> <option value="0-AM-CA"> Canadá </option> <option value="0-AF-TD"> Chad </option> <option value="0-AM-CL"> Chile </option> <option value="0-AS-CN"> China </option> <option value="0-AS-CY"> Chipre </option> <option value="0-EU-VA"> Ciudad del Vaticano </option> <option value="0-AM-CO"> Colombia </option> <option value="0-AF-KM"> Comoras </option> <option value="0-AF-CG"> Congo </option> <option value="0-AS-KP"> Corea del Norte </option> <option value="0-AS-KR"> Corea del Sur </option> <option value="0-AM-CR"> Costa Rica </option> <option value="0-AF-CI"> Costa de Marfil </option> <option value="0-EU-HR"> Croacia </option> <option value="0-AM-CU"> Cuba </option> <option value="0-EU-DK"> Dinamarca </option> <option value="0-AM-DM"> Dominica </option> <option value="0-AM-EC"> Ecuador </option> <option value="0-AF-EG"> Egipto </option> <option value="0-AM-SV"> El Salvador </option> <option value="0-AS-AE"> Emiratos Árabes Unidos </option> <option value="0-AF-ER"> Eritrea </option> <option value="0-EU-SK"> Eslovaquia </option> <option value="0-EU-SI"> Eslovenia </option> <option value="0-EU-ES"> España </option> <option value="0-AM-US"> Estados Unidos </option> <option value="0-EU-EE"> Estonia </option> <option value="0-AF-SZ"> Esuatini </option> <option value="0-AF-ET"> Etiopía </option> <option value="0-AS-PH"> Filipinas </option> <option value="0-EU-FI"> Finlandia </option> <option value="0-OC-FJ"> Fiyi </option> <option value="0-EU-FR"> Francia </option> <option value="0-AF-GA"> Gabón </option> <option value="0-AF-GM"> Gambia </option> <option value="0-AS-GE"> Georgia </option> <option value="0-AF-GH"> Ghana </option> <option value="0-EU-GI"> Gibraltar </option> <option value="0-AM-GD"> Granada </option> <option value="0-EU-GR"> Grecia </option> <option value="0-AM-GL"> Groenlandia </option> <option value="0-AM-GP"> Guadalupe </option> <option value="0-OC-GU"> Guam </option> <option value="0-AM-GT"> Guatemala </option> <option value="0-AM-GF"> Guayana Francesa </option> <option value="0-EU-GG"> Guernesey </option> <option value="0-AF-GN"> Guinea </option> <option value="0-AF-GQ"> Guinea Ecuatorial </option> <option value="0-AF-GW"> Guinea-Bisáu </option> <option value="0-AM-GY"> Guyana </option> <option value="0-AM-HT"> Haití </option> <option value="0-AM-HN"> Honduras </option> <option value="0-EU-HU"> Hungría </option> <option value="0-AS-IN"> India </option> <option value="0-AS-ID"> Indonesia </option> <option value="0-AS-IQ"> Irak </option> <option value="0-AS-IR"> Irán </option> <option value="0-EU-IE"> Irlanda </option> <option value="0-AS-CX"> Isla Christmas </option> <option value="0-OC-NF"> Isla Norfolk </option> <option value="0-EU-IM"> Isla de Man </option> <option value="0-EU-IS"> Islandia </option> <option value="0-AM-KY"> Islas Caimán </option> <option value="0-AS-CC"> Islas Cocos </option> <option value="0-OC-CK"> Islas Cook </option> <option value="0-EU-FO"> Islas Feroe </option> <option value="0-AM-FK"> Islas Malvinas </option> <option value="0-OC-MP"> Islas Marianas del Norte </option> <option value="0-OC-MH"> Islas Marshall </option> <option value="0-OC-PN"> Islas Pitcairn </option> <option value="0-OC-SB"> Islas Salomón </option> <option value="0-AM-TC"> Islas Turcas y Caicos </option> <option value="0-AM-VI"> Islas Vírgenes </option> <option value="0-AM-VG"> Islas Vírgenes Británicas </option> <option value="0-AS-IL"> Israel </option> <option value="0-EU-IT"> Italia </option> <option value="0-AM-JM"> Jamaica </option> <option value="0-AS-JP"> Japón </option> <option value="0-EU-JE"> Jersey </option> <option value="0-AS-JO"> Jordania </option> <option value="0-AS-KZ"> Kazajistán </option> <option value="0-AF-KE"> Kenia </option> <option value="0-AS-KG"> Kirguistán </option> <option value="0-OC-KI"> Kiribati </option> <option value="0-AS-KW"> Kuwait </option> <option value="0-AS-LA"> Laos </option> <option value="0-AF-LS"> Lesoto </option> <option value="0-EU-LV"> Letonia </option> <option value="0-AS-LB"> Líbano </option> <option value="0-AF-LR"> Liberia </option> <option value="0-AF-LY"> Libia </option> <option value="0-EU-LI"> Liechtenstein </option> <option value="0-EU-LT"> Lituania </option> <option value="0-EU-LU"> Luxemburgo </option> <option value="0-EU-MK"> Macedonia del Norte </option> <option value="0-AF-MG"> Madagascar </option> <option value="0-AS-MY"> Malasia </option> <option value="0-AF-MW"> Malaui </option> <option value="0-AS-MV"> Maldivas </option> <option value="0-AF-ML"> Mali </option> <option value="0-EU-MT"> Malta </option> <option value="0-AF-MA"> Marruecos </option> <option value="0-AM-MQ"> Martinica </option> <option value="0-AF-MU"> Mauricio </option> <option value="0-AF-MR"> Mauritania </option> <option value="0-AF-YT"> Mayotte </option> <option value="0-AM-MX"> México </option> <option value="0-OC-FM"> Micronesia </option> <option value="0-EU-MD"> Moldavia </option> <option value="0-EU-MC"> Mónaco </option> <option value="0-AS-MN"> Mongolia </option> <option value="0-EU-ME"> Montenegro </option> <option value="0-AM-MS"> Montserrat </option> <option value="0-AF-MZ"> Mozambique </option> <option value="0-AS-MM"> Myanmar/Birmania </option> <option value="0-AF-NA"> Namibia </option> <option value="0-OC-NR"> Nauru </option> <option value="0-AS-NP"> Nepal </option> <option value="0-AM-NI"> Nicaragua </option> <option value="0-AF-NE"> Níger </option> <option value="0-AF-NG"> Nigeria </option> <option value="0-OC-NU"> Niue </option> <option value="0-EU-NO"> Noruega </option> <option value="0-OC-NC"> Nueva Caledonia </option> <option value="0-OC-NZ"> Nueva Zelanda </option> <option value="0-AS-OM"> Omán </option> <option value="0-EU-NL"> Países Bajos </option> <option value="0-AS-PK"> Pakistán </option> <option value="0-OC-PW"> Palaos </option> <option value="0-AM-PA"> Panamá </option> <option value="0-OC-PG"> Papúa Nueva Guinea </option> <option value="0-AM-PY"> Paraguay </option> <option value="0-AM-PE"> Perú </option> <option value="0-OC-PF"> Polinesia Francesa </option> <option value="0-EU-PL"> Polonia </option> <option value="0-EU-PT"> Portugal </option> <option value="0-AM-PR"> Puerto Rico </option> <option value="0-AS-QA"> Qatar </option> <option value="0-EU-GB"> Reino Unido </option> <option value="0-AF-CF"> República Centroafricana </option> <option value="0-EU-CZ"> República Checa </option> <option value="0-AM-DO"> República Dominicana </option> <option value="0-AF-RE"> Reunión </option> <option value="0-AF-RW"> Ruanda </option> <option value="0-EU-RO"> Rumanía </option> <option value="0-AS-RU"> Rusia </option> <option value="0-AF-EH"> Sahara Occidental </option> <option value="0-OC-WS"> Samoa </option> <option value="0-OC-AS"> Samoa Americana </option> <option value="0-AM-KN"> San Cristóbal y Nieves </option> <option value="0-EU-SM"> San Marino </option> <option value="0-AM-PM"> San Pedro y Miquelón </option> <option value="0-AM-VC"> San Vicente y las Granadinas </option> <option value="0-AM-LC"> Santa Lucía </option> <option value="0-AF-ST"> Santo Tomé y Príncipe </option> <option value="0-AF-SN"> Senegal </option> <option value="0-EU-RS"> Serbia </option> <option value="0-AF-SC"> Seychelles </option> <option value="0-AF-SL"> Sierra Leona </option> <option value="0-AS-SG"> Singapur </option> <option value="0-AS-SY"> Siria </option> <option value="0-AF-SO"> Somalia </option> <option value="0-AS-LK"> Sri Lanka </option> <option value="0-AF-ZA"> Sudáfrica </option> <option value="0-AF-SD"> Sudán </option> <option value="0-EU-SE"> Suecia </option> <option value="0-EU-CH"> Suiza </option> <option value="0-AM-SR"> Surinam </option> <option value="0-EU-SJ"> Svalbard y Jan Mayen </option> <option value="0-AS-TH"> Tailandia </option> <option value="0-AS-TW"> Taiwán </option> <option value="0-AF-TZ"> Tanzania </option> <option value="0-AS-TJ"> Tayikistán </option> <option value="0-AF-TG"> Togo </option> <option value="0-OC-TK"> Tokelau </option> <option value="0-OC-TO"> Tonga </option> <option value="0-AM-TT"> Trinidad y Tobago </option> <option value="0-AF-TN"> Túnez </option> <option value="0-AS-TM"> Turkmenistán </option> <option value="0-AS-TR"> Turquía </option> <option value="0-OC-TV"> Tuvalu </option> <option value="0-EU-UA"> Ucrania </option> <option value="0-AF-UG"> Uganda </option> <option value="0-AM-UY"> Uruguay </option> <option value="0-AS-UZ"> Uzbekistán </option> <option value="0-OC-VU"> Vanuatu </option> <option value="0-AM-VE"> Venezuela </option> <option value="0-AS-VN"> Vietnam </option> <option value="0-OC-WF"> Wallis y Futuna </option> <option value="0-AS-YE"> Yemen </option> <option value="0-AF-DJ"> Yibuti </option> <option value="0-AF-ZR"> Zaire </option> <option value="0-AF-ZM"> Zambia </option> <option value="0-AF-ZW"> Zimbabue </option> </select> </label> </div> </div> </div> <div class="item-form half-items clearfix"> <div> <label> <span>Situación laboral</span> <select id="contractType" name="contractType" data-validation="select" data-message-select="Necesitamos conocer este dato"> <option value="">Seleccionar</option> <option value="1">Fijo</option> <option value="4">Temporal</option> <option value="10">Jubilado/Pensionista</option> <option value="2">Funcionario</option> <option value="3">Autónomo</option> <option value="17">Rentista</option> <option value="14">Sin actividad económica</option> <option value="0">Otros</option> </select> </label> </div> <div> <label> <span>Edad del solicitante más joven</span> <span class="desc-container"> <input type="tel" name="holderAge" data-validation="required number minimum" data-minimum="18" maxlength="2" value="" data-message-minimum="Debes ser mayor de 18 años" data-message-required="Necesitamos conocer este dato" data-message-number="Debe ser un número"/> </span> </label> </div> </div> <div class="item-form half-items clearfix"> <div> <label> <span>Ingresos familiares al mes</span> <span class="desc-container"> <input type="tel" name="monthlyIncome" data-validation="required number" maxlength="13" data-message-required="Necesitamos conocer este dato" data-message-number="Debe ser un número"/> <span class="desc">€/mes</span> </span> </label> <p class="txt-comment mortgages-extra-info"> Suma de ingresos de todos los titulares (nóminas, rentas de alquiler...) </p> </div> <div> <label> <span>Cuotas otros préstamos</span> <span class="desc-container"> <input type="tel" name="otherLoans" data-validation="number" maxlength="13" value="" data-message-number="Debe ser un número"/> <span class="desc">€/mes</span> </span> </label> <p class="txt-comment mortgages-extra-info"> Suma de gastos de otros préstamos (otras hipotecas, letra del coche...) </p> </div> </div> <div class="item-form"> <button id="next-to-step3-btn" type="button" class="btn action expand">Continuar</button> <button id="go-to-comparator" type="button" class="btn action expand d-none">Ver ofertas</button> </div> </div> <div id="no-valid-step2"> </div> <div id="contact-step3"> <div class="mortgages-info"> <span> Te ayudamos con tu hipoteca: <a href="tel:912183169" class="phoneMobile">912 183 169</a> <span class="phoneDesktop">912 183 169</span> </span> <span>Servicio gratuito y sin compromiso</span> </div> <div class="feedback contextual full-width warning icon-feedbk-alert error-container d-none"> <p>Algo no ha ido bien. Por favor, inténtalo de nuevo</p> </div> <p class="txt-bold">Déjanos tus datos y te contactamos</p> <article class="feedback full-width comparator-feedback icon-feedbk-info d-none"> </article> <div class="item-form half-items clearfix"> <div> <label> <span>Tu nombre y apellidos</span> <input type="text" name="username" maxlength="50" value="" autocomplete="on" data-validation="required" data-message-required="Necesitamos conocer este dato"/> </label> </div> <div> <label> <span class="id-tooltip"> Tu teléfono <span class="id-tooltip-message">Te llamaremos para resolver todas tus dudas. No compartimos tus datos con terceros.</span> </span> <input type="text" name="phone" value="" data-validation="required phone" data-message-required="Necesitamos conocer este dato" data-message-phone='Revisa tu teléfono' maxlength="13"/> </label> </div> </div> <div class="item-form email"> <div> <label> <span>Tu email</span> <input type="email" name="email" maxlength="100" value="" autocomplete="email" data-validation="required email" data-message-required="Necesitamos conocer este dato" data-message-email='Revisa el formato de tu email' </label> </div> </div> <div class="item-form observations"> <label> <span> ¿Tienes alguna pregunta? (Opcional) </span> <textarea maxlength="1000" name="message"></textarea> </label> </div> <div class="item-form js-data-protection js-no-dataprotection-cookie"> <label class="input-checkbox txt-small" data-validation="checkbox" data-message-checkbox="Necesitas marcar esta opción para enviar el formulario "> <input type="checkbox" name="privacyCheck" value="true" data-cookie="false"/> <span> <span> Acepto la <a target="_blank" href="/hipotecas/privacidad">política de privacidad</a> y <a target="_blank" href="/hipotecas/condiciones-contratacion">condiciones generales</a> </span> </span> </label> <label class="input-checkbox txt-small"> <input type="checkbox" name="promoCheck" value="true" data-cookie="false"/> <span> <span>Acepto recibir comunicaciones comerciales de idealista/hipotecas, por cualquier medio, y las que me remita de los sectores financiero, inmobiliario, legal y asegurador.</span> </span> </label> </div> <div class="item-form"> <input id="send-form-mortgages" type="submit" class="btn action expand" value="Analizar mi caso"> </div> </div> <section id="ready-to-buy-step"> <div class="ready-to-buy-info"> <span class="txt-soft">Te ayudamos con tu hipoteca: 912 183 169</span> <span class="txt-soft">Servicio gratuito y sin compromiso</span> </div> <div class="feedback contextual full-width warning icon-feedbk-alert error-container d-none"> <p>Algo no ha ido bien. Por favor, inténtalo de nuevo</p> </div> <p class="txt-bold user-information">Déjanos tus datos y te contactamos</p> <div class="item-form half-items clearfix"> <div> <label> <span>Tu nombre y apellidos</span> <input type="text" name="username" maxlength="50" value="" autocomplete="on" data-validation="required" data-message-required="Necesitamos conocer este dato"/> </label> </div> <div> <label> <span class="id-tooltip"> Tu teléfono <span class="id-tooltip-message">Te llamaremos para resolver todas tus dudas. No compartimos tus datos con terceros.</span> </span> <input type="text" name="phone" value="" data-validation="required phone" data-message-required="Necesitamos conocer este dato" data-message-phone='Revisa tu teléfono' maxlength="13"/> </label> </div> </div> <div class="item-form email"> <div> <label> <span>Tu email</span> <input type="email" name="email" maxlength="100" value="" autocomplete="email" data-validation="required email" data-message-required="Necesitamos conocer este dato" data-message-email='Revisa el formato de tu email' </label> </div> </div> <div class="item-form observations"> <label> <span> ¿Tienes alguna pregunta? (Opcional) </span> <textarea maxlength="1000" name="message"></textarea> </label> </div> <div class="item-form js-data-protection js-no-dataprotection-cookie legal-terms"> <label class="input-checkbox txt-small" data-validation="checkbox" data-message-checkbox="Necesitas marcar esta opción para enviar el formulario "> <input type="checkbox" name="privacyCheck" value="true" data-cookie="false"/> <span> <span> Acepto la <a target="_blank" href="/hipotecas/privacidad">política de privacidad</a> y <a target="_blank" href="/hipotecas/condiciones-contratacion">condiciones generales</a> </span> </span> </label> <label class="input-checkbox txt-small"> <input type="checkbox" name="promoCheck" value="true" data-cookie="false"/> <span> <span>Acepto recibir comunicaciones comerciales de idealista/hipotecas, por cualquier medio, y las que me remita de los sectores financiero, inmobiliario, legal y asegurador.</span> </span> </label> </div> <div class="item-form"> <input id="send-form-mortgages-rtb" type="submit" class="btn action expand" value="Analizar mi caso"> </div> </section> <div id="guides-step"> <p class="mortgages-info mortgages-guides-info"> Toda la información sobre hipotecas para resolver tus dudas y necesidades </p> <ul> <li class="mortgages-guides-list"> <a target="_blank" href='/hipotecas/informacion-sobre-hipotecas/hipoteca-fija-o-variable'>¿Hipoteca fija o variable?</a> </li> <li class="mortgages-guides-list"> <a target="_blank" href='/hipotecas/informacion-sobre-hipotecas/gastos-compraventa-vivienda'>¿Cuánto dinero suponen los gastos de la compraventa de una vivienda? </a> </li> <li class="mortgages-guides-list"> <a target="_blank" href='/hipotecas/informacion-sobre-hipotecas/conseguir-hipoteca-criterios'>Cómo conseguir una hipoteca: Criterios de concesión</a> </li> <li class="mortgages-guides-list"> <a target="_blank" href='/hipotecas/informacion-sobre-hipotecas/servicio-idealista-hipotecas'>¿Cómo funciona el servicio de idealista/hipotecas?</a> </li> <li class="mortgages-guides-list"> <a target="_blank" href='/hipotecas/informacion/guias-hipotecas/necesitas-una-hipoteca'>Ver todas las guías</a> </li> </ul> <div class="back-button-mortgages-guides"> <a id="back-to-intermediate-step-btn" class="icon-arrow-left" href="#">Volver</a> </div> </div> </div> </form> </div>
</script> <script type="text/microtemplate" id="mortgages-comparator-results-panel-tmpl">
<p> También tenemos <span>{{=totalResults}}</span> ofertas en nuestro comparador que coinciden con tus criterios. <a id="mortgages-comparator-redirect" href="{{=redirectUrl}}" target="_blank">Comparar hipotecas</a> </p>
</script> <script type="text/microtemplate" id="contact-mortgages-step1-buying-price-low-step-validation-tmpl">
<div class="feedback contextual full-width warning icon-feedbk-alert error-container"> <p>No es posible encontrar una hipoteca con esas condiciones</p> </div> <p> Los bancos con los que trabajamos fijan un <strong>valor mínimo del precio de la vivienda de {{=minBuyingPrice}}€</strong> para analizar una solicitud de hipoteca </p> <a href="#" data-simulator-url="/hipotecas/hipoteca-para-comprar-casa?adId=92174198&xtatc=INT-8-[detalle_personalizar_hipoteca_consecutivo]" class="btn action expand go-to-simulator" target="_blank"> Calcular gastos </a> <div class="back-button-mortgages"> <a class="back-step1-btn" href="#">Volver al paso anterior</a> </div>
</script> <script type="text/microtemplate" id="contact-mortgages-step1-no-savings-validation-tmpl">
<div class="feedback contextual full-width warning icon-feedbk-alert error-container"> <p>No es posible encontrar una hipoteca con esas condiciones</p> </div> <p> Los bancos suelen pedir una aportación mínima del <strong>{{=minLtvPercentage}}% del precio de compra.</strong> </p> <a href="#" data-simulator-url="/hipotecas/hipoteca-para-comprar-casa?adId=92174198&xtatc=INT-8-[detalle_personalizar_hipoteca_consecutivo]" class="btn action expand go-to-simulator" target="_blank"> Calcular gastos </a> <div class="back-button-mortgages"> <a class="back-step1-btn" href="#">Volver al paso anterior</a> </div>
</script> <script type="text/microtemplate" id="contact-mortgages-step2-contract-type-validation-tmpl">
{{hasNoActivityContract}} <div class="feedback contextual full-width warning icon-feedbk-alert error-container"> <p>No es posible encontrar una hipoteca con esas condiciones</p> </div> {{/hasNoActivityContract}} <p> Los bancos requieren que al menos uno de los solicitantes tenga <strong>estabilidad laboral demostrable</strong> para conceder una hipoteca. </p> {{hasTemporalContract}} <button id="temporal-to-step3-btn" type="button" class="btn action expand">Continuar</button> {{/hasTemporalContract}} <div class="back-button-mortgages"> <a id="back-step2-btn" href="#">Volver al paso anterior</a> </div>
</script> <script type="text/microtemplate" id="contact-mortgages-step2-validation-tmpl">
<div class="feedback contextual full-width warning icon-feedbk-alert error-container"> <p>No es posible encontrar una hipoteca con esas condiciones</p> </div> {{showAllErrors}} <ul class="styled-list"> <li> Los bancos no permiten que la <strong>edad</strong> del solicitante más joven sea <strong>superior a los 65 años.</strong> </li> <li> Para esta casa y con tu ahorro aportado, tendrías que pagar una mensualidad de <strong>{{=monthlyRate}}€ durante 30 años.</strong> Al superar el 40% de tus ingresos los bancos no te concederán la hipoteca. </li> </ul> {{:showAllErrors}} {{ageValid}} {{:ageValid}} <p> Los bancos no permiten que la <strong>edad</strong> del solicitante más joven sea <strong>superior a los 65 años.</strong> </p> {{/ageValid}} {{debtRatioValid}} {{:debtRatioValid}} <p> Para esta casa y con tu ahorro aportado, tendrías que pagar una mensualidad de <strong>{{=monthlyRate}}€ durante 30 años.</strong> Al superar el 40% de tus ingresos los bancos no te concederán la hipoteca. </p> {{/debtRatioValid}} {{/showAllErrors}} <div class="back-button-mortgages"> <a id="back-step2-btn" href="#">Volver al paso anterior</a> </div>
</script> <script type="text/microtemplate" id="contact-mortgages-step2-low-income-validation-tmpl">
<div class="feedback contextual full-width warning icon-feedbk-alert error-container"> <p>No es posible encontrar una hipoteca con esas condiciones</p> </div> <p> Los bancos con los que trabajamos requieren unos <strong>ingresos mínimos mensuales de 800€</strong> para conceder una hipoteca. </p> <a href="#" data-simulator-url="" class="btn action expand go-to-simulator" target="_blank"> Calcular gastos </a> <div class="back-button-mortgages"> <a id="back-step2-btn" href="#">Volver al paso anterior</a> </div>
</script> <script type="text/microtemplate" id="contact-mortgages-step2-contract-type-validation-tmpl">
{{hasNoActivityContract}} <div class="feedback contextual full-width warning icon-feedbk-alert error-container"> <p>No es posible encontrar una hipoteca con esas condiciones</p> </div> {{/hasNoActivityContract}} <p> Los bancos requieren que al menos uno de los solicitantes tenga <strong>estabilidad laboral demostrable</strong> para conceder una hipoteca. </p> {{hasTemporalContract}} <button id="temporal-to-step3-btn" type="button" class="btn action expand">Continuar</button> {{/hasTemporalContract}} <div class="back-button-mortgages"> <a id="back-step2-btn" href="#">Volver al paso anterior</a> </div>
</script> <script type="text/microtemplate" id="contact-mortgages-step2-country-validation-tmpl">
<div class="feedback contextual full-width warning icon-feedbk-alert error-container"> <p>No es posible encontrar una hipoteca con esas condiciones</p> </div> <p> No tenemos productos para residentes en {{=countrySelected}} <a href="{{=invalidCountryGuide}}" target="_blank">¿Por qué?</a> </p> <div class="back-button-mortgages"> <a id="back-step2-btn" href="#">Volver al paso anterior</a> </div>
</script> <script type="text/microtemplate" id="show-more-photos-tmpl">
<div id="show-more-photos-button" class="button overlay-box"> <a class="btn regular more-photos" data-markup="seePhotos"> Ver {{=hiddenPhotosLeft}} fotos siguientes </a> </div>
</script> <script type="text/microtemplate" id="saving-alert-tmpl">
{{lessSavingAlert}} Necesitas cubrir la aportación mínima que piden los bancos. <a id="link-set-saving" href="" data-markup='detalle::mortgages::savings-adjust'>Ajustar</a>. {{/lessSavingAlert}} {{bigSavingWarning}} El ahorro aportado no puede ser mayor que el precio del inmueble {{/bigSavingWarning}}
</script> <script type="text/microtemplate" id="saving-warning-tmpl">
El ahorro aportado no puede ser mayor que el precio del inmueble
</script> <script type="text/microtemplate" id="expenses-glossary-tmpl">
<div id="expenses-glossary-content" class="expenses-glossary"> <p class="h3-simulated txt-bold mb-regular">Impuestos y gastos</p> <p>Cálculo teniendo en cuenta la localización y condición del inmueble.</p> <table id="buying-expenses"> <thead> <tr> <th class="txt-bold" colspan="3">Gastos de la Compra </th> </tr> </thead> <tbody> <tr> <td>Notaría : </td> <td class="separator"><span></span></td> <td class="price"><span>{{=buyingExpenses.notaryExpenses}} &euro;</span></td> </tr> <tr> <td>Registro: </td> <td class="separator"><span></span></td> <td class="price"><span>{{=buyingExpenses.registryExpenses}} &euro;</span></td> </tr> <tr> <td>Gestoría: </td> <td class="separator"><span></span></td> <td class="price"><span>{{=buyingExpenses.processAgencyExpenses}} &euro;</span></td> </tr> <tr> <td>Impuestos: </td> <td class="separator"><span></span></td> <td class="price"><span>{{=buyingExpenses.taxesExpenses.total}} &euro;</span></td> </tr> <tr class="total"> <td class="result">Total: </td> <td class="separator"><span></span></td> <td class="price"><span>{{=buyingExpenses.total}} &euro;</span></td> </tr> </tbody> </table> <table id="loan-expenses"> <thead> <tr> <th class="txt-bold" colspan="3">Gastos de la Hipoteca</th> </tr> </thead> <tbody> <tr> <td>Notaría : </td> <td class="separator"><span></span></td> <td class="price"><span>{{=loanExpenses.notaryExpenses}} &euro;</span></td> </tr> <tr> <td>Registro: </td> <td class="separator"><span></span></td> <td class="price"><span>{{=loanExpenses.registryExpenses}} &euro;</span></td> </tr> <tr> <td>Gestoría: </td> <td class="separator"><span></span></td> <td class="price"><span>{{=loanExpenses.processAgencyExpenses}} &euro;</span></td> </tr> <tr> <td>Impuestos: </td> <td class="separator"><span></span></td> <td class="price"><span>{{=loanExpenses.taxesExpenses.total}} &euro;</span></td> </tr> <tr class="total"> <td class="result">Total: </td> <td class="separator"><span></span></td> <td class="price"><span>{{=loanExpenses.total}} &euro;</span></td> </tr> </tbody> </table> <table id="results"> <tbody> <tr class="total"> <td><strong>Gastos totales</strong></td> <td class="separator"><span></span></td> <td class="price"><strong>{{=totalExpenses}} &euro;</strong></td> </tr> <tr> <td>Gastos de Tasación </td> <td class="separator"><span></span></td> <td class="price"><span>{{=tasationExpenses}} &euro;</span></td> </tr> </tbody> </table> <p class="txt-result">Este es un cálculo orientativo no vinculante. Es necesario remitir la información detallada al banco para calcular la cantidad exacta.</p> </div>
</script> <script type="text/microtemplate" id="rate-type-info-tmpl">
<div class="rate-type-info-content"> <h3 class="txt-medium txt-bold mb-small">¿Cómo hemos calculado el interés?</h3> <p>Este tipo de interés es una referencia media del mercado hipotecario.</p> <p><strong>Fijo: </strong>es el tipo de interés que se mantiene constante durante toda la vida del préstamo. Es más bajo cuando menor sea el plazo.</p> <p><strong>Variable: </strong>la cuota variará a lo largo de la vida del préstamo en función a las fluctuaciones del euribor</p> </div>
</script> <script type="text/microtemplate" id="affida-contact-mortgages-tmpl" data-title="">
<section class="affida-contact"> <div class="modal-inner"> <form id="send-contact-form" method="post" novalidate> <div class="logo-idealista"> <a href="/hipotecas/"> <span class="icon-idealista-icon"></span> <span class="icon-hipotecas"></span> </a> </div> <div class="item-form affida-items"> <div class="item-form"> <label> <span>Precio inmueble</span> <span class="desc-container"> <span class="desc">Euros</span> <input type="tel" class="property-price" name="property-price" maxlength="9" data-min="0" data-max="2000000" data-validation="required number" data-message-required="Necesitamos conocer este dato" value="17900"> </span> </label> </div> <div class="item-form"> <label> <span class="show-mobile">Ahorro aportado</span> <span class="show-desktop">Ahorro aportado</span> <span class="desc-container"> <span class="desc">Euros</span> <input type="tel" class="savings-input" name="savings" maxlength="9" data-min="0" data-max="2000000" data-validation="required number" data-message-required="Necesitamos conocer este dato" value="{{=savings}}"/> </span> </label> </div> <div class="item-form"> <label> <span>Term (years)</span> <select class="duration-in-years" name="duration-in-years" data-validation="select" data-message-select="Necesitamos conocer este dato"> <option value="">Seleccionar</option> <option value="30" {{y30}} selected {{/y30}}>30</option> <option value="25" {{y25}} selected {{/y25}}>25</option> <option value="20" {{y20}} selected {{/y20}}>20</option> <option value="15" {{y15}} selected {{/y15}}>15</option> <option value="10" {{y10}} selected {{/y10}}>10</option> </select> </label> </div> <div class="item-form"> <label> Tipo de interés <ul class="horizontal-list"> <li> <label class="input-radio"> <input type="radio" name="tax-type" value="2" {{variableRate}} checked="checked" {{/variableRate}}> <span> <span>Variable</span> </span> </label> </li> <li> <label class="input-radio"> <input type="radio" name="tax-type" value="1" {{fixedRate}} checked="checked" {{/fixedRate}}> <span> <span>Fijo</span> </span> </label> </li> </ul> </label> </div> </div> <div class="affida-offer"> <div class="affida-offer__main"> <p class="txt-big">Nuestra mejor oferta</p> <p class="txt-bold txt-big affida-offer__monthly"><span id="js-calculated-monthly">3901</span> €/mes</p> </div> <div class="affida-offer__details"> <p> <span class="affida-offer__details__condition">Ahorro aportado:</span> <span><span id="js-calculated-savings"></span>€</span> </p> <p> <span class="affida-offer__details__condition">Importe hipoteca:</span> <span><span id="js-calculated-loan"></span>€</span> </p> <p> <span class="affida-offer__details__condition">Interés:</span> <span><span id="js-calculated-tax"></span>%</span> </p> </div> </div> <div class="affida-offer__btn-container"> <a href="#" id="js-affida-form" class="btn action fullwidth send-form">Encuentra tu hipoteca </a> </div> </form> <div class="affida-legal"> <p>Affida S.r.l. El tipo de interés indicado es el más ventajoso disponible entre las entidades de crédito que mantienen acuerdos con Affida S.r.l. y es el resultado de un cálculo algorítmico de los préstamos hipotecarios. La cuota se publica sólo a título informativo y puede ser confirmada o recalculada tras el asesoramiento y el análisis de Affida S.r.l. y de acuerdo con los parámetros de crédito de los bancos y/u otros acreedores. Póngase en contacto con uno de los más de 200 agentes personales de Affida presentes en todo el territorio nacional para obtener asesoramiento y el cupón "La Casa en Tasca", un documento oficial emitido por una institución financiera socia de Affida S.r.l. que le dará la certeza de obtener el préstamo hipotecario en las condiciones establecidas. Las hojas de información están disponibles en la red de agencias de Affida S.r.l. o en el sitio web de Affida.credit </p> </div> </div> </section>
</script> <script type="text/microtemplate" id="comment-translation-tmpl">
<div class="translationWarning"> <span>{{=message}}</span> <span class="viewOriginalComment"><a href="#" data-lang="es">{{=messageLink}}</a></span> <div class="googleTranslateLogo"><img src="https://st1.idealista.com/static/common/release/detail/resources/img/google-translate.png"></div> </div>
</script> <script type="text/microtemplate" id="comment-tmpl">
<div class="adCommentsLanguage expandable" data-compressed-max-length="1650"> {{=commentText}} </div> {{expandable}} <div class="expanderParent"> <span class="icon-arrow-right"></span> <a href="#" class="expander">Leer comentario completo</a> </div> {{/expandable}}
</script> <script type="text/javascript" async src="https://st1.idealista.com/static/common/js/criteo/ld.js?20201222-0907"></script> </body> </html>
"""
return html
@pytest.fixture
def unrelated_html():
html = """
<!doctype html>
<html lang="en">
<head>
<link href="https://files.realpython.com" rel="preconnect">
<title>Primer on Python Decorators Real Python</title>
<meta name="author" content="Real Python">
<meta name="description" content="In this introductory tutorial, we&#x27;ll look at what Python decorators are and how to create and use them.">
<meta name="keywords" content="">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="/static/gfonts/font.32be62914940.css">
<link rel="stylesheet" href="/static/realpython.min.afcaac4617ad.css">
<link rel="canonical" href="https://realpython.com/primer-on-python-decorators/">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="https://files.realpython.com/media/Primer-on-Python-Decorators_Watermarked.d0da542fa3fc.jpg">
<meta property="og:image" content="https://files.realpython.com/media/Primer-on-Python-Decorators_Watermarked.d0da542fa3fc.jpg">
<meta name="twitter:creator" content="@realpython">
<meta name="twitter:site" content="@realpython">
<meta property="og:title" content="Primer on Python Decorators Real Python">
<meta property="og:type" content="article">
<meta property="og:url" content="https://realpython.com/primer-on-python-decorators/">
<meta property="og:description" content="In this introductory tutorial, we&#x27;ll look at what Python decorators are and how to create and use them.">
<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>
(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', 'anonymizeIp', true);
ga('set', {
dimension1: false,
dimension2: false
});
ga('send', 'pageview');
</script>
<script async src='/cdn-cgi/bm/cv/669835187/api.js'></script></head>
<body>
<nav class="navbar fixed-top navbar-expand-lg navbar-dark flex-column nav-message">
<div class="container flex-row">
<a class="navbar-brand" href="/">
<img src="/static/real-python-logo.893c30edea53.svg" 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">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-2 flex-fill">
<li class="nav-item">
<a class="nav-link" href="/start-here/">Start&nbsp;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="fa fa-graduation-cap"></span> Learn Python
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdownLibrary">
<a class="dropdown-item" href="/" style="color: #ff7e73; line-height: 110%;"><i class="fa fa-fw mr-1 fa-graduation-cap"></i> Python Tutorials →<br><small class="text-secondary">In-depth articles and tutorials</small></a>
<a class="dropdown-item" href="/courses/" style="color: #abe5b1; line-height: 110%;"><i class="fa fa-fw mr-1 fa-film"></i> Video Courses →<br><small class="text-secondary">Step-by-step video lessons</small></a>
<a class="dropdown-item" href="/quizzes/" style="color: #abe0e5; line-height: 110%;"><i class="fa fa-fw mr-1 fa-trophy"></i> Quizzes →<br><small class="text-secondary">Check your learning progress</small></a>
<a class="dropdown-item" href="/learning-paths/" style="color: #ffc873; line-height: 110%;"><i class="fa fa-fw mr-1 fa-map-o"></i> Learning Paths →<br><small class="text-secondary">Guided study plans for accelerated learning</small></a>
<a class="dropdown-item" href="/community/" style="color: #e5c6ab; line-height: 110%;"><i class="fa fa-fw mr-1 fa-slack"></i> Community →<br><small class="text-secondary">Learn with other Pythonistas</small></a>
<a class="dropdown-item pb-3" href="/tutorials/all/" style="color: #b8abe5; line-height: 110%;"><i class="fa fa-fw mr-1 fa-tags"></i> Topics →<br><small class="text-secondary">Focus on a specific area or skill level</small></a>
<a class="dropdown-item border-top" href="/account/join/"><i class="fa fa-fw fa-star text-warning"></i> Unlock All Content</a>
</div>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownBooksCourses" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Store
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdownBooksCourses">
<a class="dropdown-item" href="/account/join/"><i class="fa fa-fw fa-star text-warning"></i> RP Membership</a>
<a class="dropdown-item" href="/products/python-basics-book/">Python Basics Book</a>
<a class="dropdown-item" href="/products/python-tricks-book/">Python Tricks Book</a>
<a class="dropdown-item" href="/products/cpython-internals-book/">CPython Internals Book</a>
<a class="dropdown-item" href="/products/real-python-course/">The Real Python Course</a>
<a class="dropdown-item" href="/products/managing-python-dependencies/">Managing Python Dependencies</a>
<a class="dropdown-item" href="/products/sublime-python/">Sublime Text + Python Setup</a>
<a class="dropdown-item" href="/products/pythonic-wallpapers/">Pythonic Wallpapers Pack</a>
<a class="dropdown-item" href="https://nerdlettering.com" target="_blank">Python Mugs, T-Shirts, and More</a>
<a class="dropdown-item" href="https://www.pythonistacafe.com" target="_blank">Pythonista Cafe Community</a>
<a class="dropdown-item border-top" href="/products/">Browse All »</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="/newsletter/">Python Newsletter</a>
<a class="dropdown-item" href="/podcasts/rpp/">Python Podcast</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 Author</a>
<a class="dropdown-item" href="/become-an-instructor/">Become a Video Instructor</a>
</div>
</li>
</ul>
<div class="d-block d-xl-none">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="/search" title="Search"><span class="d-block d-lg-none"><i class="fa fa-search"></i> Search</span><span class="d-none d-lg-block"><i class="fa fa-search"></i></span></a>
</li>
</ul>
</div>
<div class="d-none d-xl-flex align-items-center mr-2">
<form class="form-inline" action="/search" method="GET">
<a class="position-absolute" href="/search" title="Search"><i class="fa fa-search fa-fw text-muted pl-2"></i></a>
<input class="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" name="q">
<input type="hidden" name="_from" value="nav">
</form>
</div>
<ul class="navbar-nav">
<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/">Sign&#8209;In</a>
</li>
</ul>
</div>
</div>
<div class="flex-row w-100" style="border-top: 1px dashed #ffc107 !important;">
<div class="container">
<a class="link-unstyled mx-auto" href="https://realpython.com/free-courses-march-2020"><p class="my-1 small"><span class="text-warning">😷 <strong>Stuck at home?</strong> Enjoy free courses, on&nbsp;us&nbsp;→</span></p></a>
</div>
</div>
</nav>
<div class="container main-content">
<div class="row justify-content-center">
<div class="col-md-11 col-lg-8 article with-headerlinks">
<figure class="embed-responsive embed-responsive-16by9">
<img class="card-img-top m-0 p-0 embed-responsive-item rounded" style="object-fit: contain;" alt="Python Decorators" src="https://files.realpython.com/media/Primer-on-Python-Decorators_Watermarked.d0da542fa3fc.jpg" width="1920" height="1080" srcset="https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Primer-on-Python-Decorators_Watermarked.d0da542fa3fc.jpg&amp;w=480&amp;sig=45483afc98ba9e9ed26f1672d17d426e011761df 480w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Primer-on-Python-Decorators_Watermarked.d0da542fa3fc.jpg&amp;w=960&amp;sig=5b4082dbad29a1c1f1fdbe7b5de93af202663c6a 960w, https://files.realpython.com/media/Primer-on-Python-Decorators_Watermarked.d0da542fa3fc.jpg 1920w" sizes="75vw">
</figure>
<h1>Primer on Python Decorators</h1>
<p class="mb-0">
<span class="text-muted">by <a class="text-muted" href="#author">Geir Arne Hjelle</a>
<span class="ml-2 mr-1 fa fa-comments"></span><a class="text-muted" href="#reader-comments"><span class="disqus-comment-count" data-disqus-identifier="https://realpython.com/primer-on-python-decorators/"></span></a>
<span class="ml-2 fa fa-tags"></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>
<div class="d-flex flex-row justify-content-between my-2">
<div class="align-self-center">
<span>
<a target="_blank" rel="nofollow" href="https://twitter.com/intent/tweet/?text=Check out this %23Python tutorial: Primer%20on%20Python%20Decorators by @realpython&url=https%3A//realpython.com/primer-on-python-decorators/" class="mr-1 badge badge-twitter text-light mb-1"><i class="mr-1 fa fa-twitter text-light"></i>Tweet</a>
<a target="_blank" rel="nofollow" href="https://facebook.com/sharer/sharer.php?u=https%3A//realpython.com/primer-on-python-decorators/" class="mr-1 badge badge-facebook text-light mb-1"><i class="mr-1 fa fa-facebook text-light"></i>Share</a>
<a target="_blank" rel="nofollow" href="mailto:?subject=Python article for you&body=Check out this Python tutorial:%0A%0APrimer%20on%20Python%20Decorators%0A%0Ahttps%3A//realpython.com/primer-on-python-decorators/" class="mr-1 badge badge-red text-light mb-1"><i class="mr-1 fa fa-envelope text-light"></i>Email</a>
</span>
</div>
</div>
</p>
<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="#functions">Functions</a><ul>
<li><a href="#first-class-objects">First-Class Objects</a></li>
<li><a href="#inner-functions">Inner Functions</a></li>
<li><a href="#returning-functions-from-functions">Returning Functions From Functions</a></li>
</ul>
</li>
<li><a href="#simple-decorators">Simple Decorators</a><ul>
<li><a href="#syntactic-sugar">Syntactic Sugar!</a></li>
<li><a href="#reusing-decorators">Reusing Decorators</a></li>
<li><a href="#decorating-functions-with-arguments">Decorating Functions With Arguments</a></li>
<li><a href="#returning-values-from-decorated-functions">Returning Values From Decorated Functions</a></li>
<li><a href="#who-are-you-really">Who Are You, Really?</a></li>
</ul>
</li>
<li><a href="#a-few-real-world-examples">A Few Real World Examples</a><ul>
<li><a href="#timing-functions">Timing Functions</a></li>
<li><a href="#debugging-code">Debugging Code</a></li>
<li><a href="#slowing-down-code">Slowing Down Code</a></li>
<li><a href="#registering-plugins">Registering Plugins</a></li>
<li><a href="#is-the-user-logged-in">Is the User Logged In?</a></li>
</ul>
</li>
<li><a href="#fancy-decorators">Fancy Decorators</a><ul>
<li><a href="#decorating-classes">Decorating Classes</a></li>
<li><a href="#nesting-decorators">Nesting Decorators</a></li>
<li><a href="#decorators-with-arguments">Decorators With Arguments</a></li>
<li><a href="#both-please-but-never-mind-the-bread">Both Please, But Never Mind the Bread</a></li>
<li><a href="#stateful-decorators">Stateful Decorators</a></li>
<li><a href="#classes-as-decorators">Classes as Decorators</a></li>
</ul>
</li>
<li><a href="#more-real-world-examples">More Real World Examples</a><ul>
<li><a href="#slowing-down-code-revisited">Slowing Down Code, Revisited</a></li>
<li><a href="#creating-singletons">Creating Singletons</a></li>
<li><a href="#caching-return-values">Caching Return Values</a></li>
<li><a href="#adding-information-about-units">Adding Information About Units</a></li>
<li><a href="#validating-json">Validating JSON</a></li>
</ul>
</li>
<li><a href="#conclusion">Conclusion</a></li>
<li><a href="#further-reading">Further Reading</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" data-unit="8x1" style="position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;"></div>
</div>
</div>
<div class="border rounded p-3 card mb-2">
<p class="mb-0"><span class="badge badge-pill badge-success"><i class="fa fa-play-circle" aria-hidden="true"></i> 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/python-decorators-101/"><strong>Python Decorators 101</strong></a></p>
</div>
<p>In this tutorial on decorators, we&rsquo;ll look at what they are and how to create and use them. Decorators provide a simple syntax for calling <a href="http://en.wikipedia.org/wiki/Higher-order_function">higher-order functions</a>.</p>
<p>By definition, a decorator is a function that takes another function and extends the behavior of the latter function without explicitly modifying it.</p>
<p>This sounds confusing, but it&rsquo;s really not, especially after you&rsquo;ve seen a few examples of how decorators work. You can find all the examples from this article <a href="https://github.com/realpython/materials/tree/master/primer-on-python-decorators">here</a>.</p>
<div class="alert alert-warning" role="alert"><p><strong>Free Bonus:</strong> <a href="" class="alert-link" data-toggle="modal" data-target="#modal-power-of-decorators-fixed" data-focus="false">Click here to get access to a free "The Power of Python Decorators" guide</a> that shows you 3 advanced decorator patterns and techniques you can use to write to cleaner and more Pythonic programs.</p></div>
<div class="alert alert-warning" role="alert">
<p><strong markdown="1">Decorators Cheat Sheet:</strong> <a href="https://realpython.com/bonus/decorators-cheatsheet/" class="alert-link" data-toggle="modal" data-target="#modal-decorators-cheatsheet" data-focus="false" markdown="1">Click here to get access to a free 3-page Python decorators cheat sheet</a> that summarizes the techniques explained in this tutorial.</p>
</div>
<div class="alert alert-warning" role="alert">
<p><strong markdown="1">Decorators Q&amp;A Transcript:</strong> <a href="https://realpython.com/bonus/decorators-qa-2019/" class="alert-link" data-toggle="modal" data-target="#modal-decorators-qa-2019" data-focus="false" markdown="1">Click here to get access to a 25-page chat log from our recent Python decorators Q&amp;A session</a> in the Real Python Community Slack where we discussed common decorator questions.</p>
</div>
<p><strong>Updates:</strong></p>
<ul>
<li><em>08/22/2018:</em> Major update adding more examples and more advanced decorators</li>
<li><em>01/12/2016:</em> Updated examples to Python 3 (v3.5.1) syntax and added a new example</li>
<li><em>11/01/2015:</em> Added a brief explanation on the <code>functools.wraps()</code> decorator</li>
</ul>
<div class="w-100 text-center js-needs-scaling" style="transform-origin: 0 0;"><div id="waldo-tag-4996"></div> <a class="small text-muted js-disclosure" href="/account/join/" rel="nofollow" style="display: none;"> <i aria-hidden="true" class="fa fa-info-circle"> </i> Remove ads</a></div><section class="section2" id="functions"><h2>Functions<a class="headerlink" href="#functions" title="Permanent link"></a></h2>
<p>Before you can understand decorators, you must first understand how functions work. For our purposes, <strong>a function returns a value based on the given arguments</strong>. Here is a very simple example:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">add_one</span><span class="p">(</span><span class="n">number</span><span class="p">):</span>
<span class="gp">... </span> <span class="k">return</span> <span class="n">number</span> <span class="o">+</span> <span class="mi">1</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">add_one</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="go">3</span>
</code></pre></div>
<p>In general, functions in Python may also have side effects rather than just turning an input into an output. <a href="https://realpython.com/python-print/">The <code>print()</code> function</a> is a basic example of this: it <a href="https://realpython.com/python-return-statement/">returns</a> <a href="https://realpython.com/null-in-python/"><code>None</code></a> while having the side effect of outputting something to the console. However, to understand decorators, it is enough to think about functions as something that turns given arguments into a value.</p>
<div class="alert alert-primary" role="alert">
<p><strong>Note:</strong> In <a href="https://en.wikipedia.org/wiki/Functional_programming">functional programming</a>, you work (almost) only with pure functions without side effects. While not a purely functional language, Python supports many of the functional programming concepts, including functions as first-class objects.</p>
</div>
<section class="section3" id="first-class-objects"><h3>First-Class Objects<a class="headerlink" href="#first-class-objects" title="Permanent link"></a></h3>
<p>In Python, functions are <a href="https://dbader.org/blog/python-first-class-functions">first-class objects</a>. This means that <strong>functions can be passed around and used as arguments</strong>, just like <a href="https://realpython.com/python-data-types/">any other object (string, int, float, list, and so on)</a>. Consider the following three functions:</p>
<div class="highlight python"><pre><span></span><code><span class="k">def</span> <span class="nf">say_hello</span><span class="p">(</span><span class="n">name</span><span class="p">):</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">&quot;Hello </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="k">def</span> <span class="nf">be_awesome</span><span class="p">(</span><span class="n">name</span><span class="p">):</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">&quot;Yo </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">, together we are the awesomest!&quot;</span>
<span class="k">def</span> <span class="nf">greet_bob</span><span class="p">(</span><span class="n">greeter_func</span><span class="p">):</span>
<span class="k">return</span> <span class="n">greeter_func</span><span class="p">(</span><span class="s2">&quot;Bob&quot;</span><span class="p">)</span>
</code></pre></div>
<p>Here, <code>say_hello()</code> and <code>be_awesome()</code> are regular functions that expect a name given as a string. The <code>greet_bob()</code> function however, expects a function as its argument. We can, for instance, pass it the <code>say_hello()</code> or the <code>be_awesome()</code> function:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">greet_bob</span><span class="p">(</span><span class="n">say_hello</span><span class="p">)</span>
<span class="go">&#39;Hello Bob&#39;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">greet_bob</span><span class="p">(</span><span class="n">be_awesome</span><span class="p">)</span>
<span class="go">&#39;Yo Bob, together we are the awesomest!&#39;</span>
</code></pre></div>
<p>Note that <code>greet_bob(say_hello)</code> refers to two functions, but in different ways: <code>greet_bob()</code> and <code>say_hello</code>. The <code>say_hello</code> function is named without parentheses. This means that only a reference to the function is passed. The function is not executed. The <code>greet_bob()</code> function, on the other hand, is written with parentheses, so it will be called as usual.</p>
</section><section class="section3" id="inner-functions"><h3>Inner Functions<a class="headerlink" href="#inner-functions" title="Permanent link"></a></h3>
<p>It&rsquo;s possible to <a href="https://realpython.com/defining-your-own-python-function/">define functions</a> <em>inside other functions</em>. Such functions are called <a href="https://realpython.com/inner-functions-what-are-they-good-for/">inner functions</a>. Here&rsquo;s an example of a function with two inner functions:</p>
<div class="highlight python"><pre><span></span><code><span class="k">def</span> <span class="nf">parent</span><span class="p">():</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Printing from the parent() function&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">first_child</span><span class="p">():</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Printing from the first_child() function&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">second_child</span><span class="p">():</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Printing from the second_child() function&quot;</span><span class="p">)</span>
<span class="n">second_child</span><span class="p">()</span>
<span class="n">first_child</span><span class="p">()</span>
</code></pre></div>
<p>What happens when you call the <code>parent()</code> function? Think about this for a minute. The output will be as follows:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">parent</span><span class="p">()</span>
<span class="go">Printing from the parent() function</span>
<span class="go">Printing from the second_child() function</span>
<span class="go">Printing from the first_child() function</span>
</code></pre></div>
<p>Note that the order in which the inner functions are defined does not matter. Like with any other functions, the printing only happens when the inner functions are executed.</p>
<p>Furthermore, the inner functions are not defined until the parent function is called. They are locally scoped to <code>parent()</code>: they only exist inside the <code>parent()</code> function as local <a href="https://realpython.com/python-variables/">variables</a>. Try calling <code>first_child()</code>. You should get an error:</p>
<div class="highlight python"><pre><span></span><code><span class="gt">Traceback (most recent call last):</span>
File <span class="nb">&quot;&lt;stdin&gt;&quot;</span>, line <span class="m">1</span>, in <span class="n">&lt;module&gt;</span>
<span class="gr">NameError</span>: <span class="n">name &#39;first_child&#39; is not defined</span>
</code></pre></div>
<p>Whenever you call <code>parent()</code>, the inner functions <code>first_child()</code> and <code>second_child()</code> are also called. But because of their local scope, they aren&rsquo;t available outside of the <code>parent()</code> function.</p>
<div class="w-100 text-center js-needs-scaling" style="transform-origin: 0 0;"><div id="waldo-tag-4998"></div> <a class="small text-muted js-disclosure" href="/account/join/" rel="nofollow" style="display: none;"> <i aria-hidden="true" class="fa fa-info-circle"> </i> Remove ads</a></div></section><section class="section3" id="returning-functions-from-functions"><h3>Returning Functions From Functions<a class="headerlink" href="#returning-functions-from-functions" title="Permanent link"></a></h3>
<p>Python also allows you to use functions as return values. The following example returns one of the inner functions from the outer <code>parent()</code> function:</p>
<div class="highlight python"><pre><span></span><code><span class="k">def</span> <span class="nf">parent</span><span class="p">(</span><span class="n">num</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">first_child</span><span class="p">():</span>
<span class="k">return</span> <span class="s2">&quot;Hi, I am Emma&quot;</span>
<span class="k">def</span> <span class="nf">second_child</span><span class="p">():</span>
<span class="k">return</span> <span class="s2">&quot;Call me Liam&quot;</span>
<span class="k">if</span> <span class="n">num</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
<span class="k">return</span> <span class="n">first_child</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="n">second_child</span>
</code></pre></div>
<p>Note that you are returning <code>first_child</code> without the parentheses. Recall that this means that you are <strong>returning a reference to the function <code>first_child</code></strong>. In contrast <code>first_child()</code> with parentheses refers to the result of evaluating the function. This can be seen in the following example:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">first</span> <span class="o">=</span> <span class="n">parent</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">second</span> <span class="o">=</span> <span class="n">parent</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">first</span>
<span class="go">&lt;function parent.&lt;locals&gt;.first_child at 0x7f599f1e2e18&gt;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">second</span>
<span class="go">&lt;function parent.&lt;locals&gt;.second_child at 0x7f599dad5268&gt;</span>
</code></pre></div>
<p>The somewhat cryptic output simply means that the <code>first</code> variable refers to the local <code>first_child()</code> function inside of <code>parent()</code>, while <code>second</code> points to <code>second_child()</code>.</p>
<p>You can now use <code>first</code> and <code>second</code> as if they are regular functions, even though the functions they point to can&rsquo;t be accessed directly:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">first</span><span class="p">()</span>
<span class="go">&#39;Hi, I am Emma&#39;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">second</span><span class="p">()</span>
<span class="go">&#39;Call me Liam&#39;</span>
</code></pre></div>
<p>Finally, note that in the earlier example you executed the inner functions within the parent function, for instance <code>first_child()</code>. However, in this last example, you did not add parentheses to the inner functions&mdash;<code>first_child</code>&mdash;upon returning. That way, you got a reference to each function that you could call in the future. Make sense?</p>
</section></section><section class="section2" id="simple-decorators"><h2>Simple Decorators<a class="headerlink" href="#simple-decorators" title="Permanent link"></a></h2>
<p>Now that you&rsquo;ve seen that functions are just like any other object in Python, you&rsquo;re ready to move on and see the magical beast that is the Python decorator. Let&rsquo;s start with an example:</p>
<div class="highlight python"><pre><span></span><code><span class="k">def</span> <span class="nf">my_decorator</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="nb">print</span><span class="p">(</span><span class="s2">&quot;Something is happening before the function is called.&quot;</span><span class="p">)</span>
<span class="n">func</span><span class="p">()</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Something is happening after the function is called.&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="n">wrapper</span>
<span class="k">def</span> <span class="nf">say_whee</span><span class="p">():</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Whee!&quot;</span><span class="p">)</span>
<span class="n">say_whee</span> <span class="o">=</span> <span class="n">my_decorator</span><span class="p">(</span><span class="n">say_whee</span><span class="p">)</span>
</code></pre></div>
<p>Can you guess what happens when you call <code>say_whee()</code>? Try it:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">say_whee</span><span class="p">()</span>
<span class="go">Something is happening before the function is called.</span>
<span class="go">Whee!</span>
<span class="go">Something is happening after the function is called.</span>
</code></pre></div>
<p>To understand what&rsquo;s going on here, look back at the previous examples. We are literally just applying everything you have learned so far.</p>
<p>The so-called decoration happens at the following line:</p>
<div class="highlight python"><pre><span></span><code><span class="n">say_whee</span> <span class="o">=</span> <span class="n">my_decorator</span><span class="p">(</span><span class="n">say_whee</span><span class="p">)</span>
</code></pre></div>
<p>In effect, the name <code>say_whee</code> now points to the <code>wrapper()</code> inner function. Remember that you return <code>wrapper</code> as a function when you call <code>my_decorator(say_whee)</code>:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">say_whee</span>
<span class="go">&lt;function my_decorator.&lt;locals&gt;.wrapper at 0x7f3c5dfd42f0&gt;</span>
</code></pre></div>
<p>However, <code>wrapper()</code> has a reference to the original <code>say_whee()</code> as <code>func</code>, and calls that function between the two calls to <a href="https://realpython.com/courses/python-print/"><code>print()</code></a>.</p>
<p>Put simply: <strong>decorators wrap a function, modifying its behavior.</strong></p>
<p>Before moving on, let&rsquo;s have a look at a second example. Because <code>wrapper()</code> is a regular Python function, the way a decorator modifies a function can change dynamically. So as not to disturb your neighbors, the following example will only run the decorated code during the day:</p>
<div class="highlight python"><pre><span></span><code><span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span>
<span class="k">def</span> <span class="nf">not_during_the_night</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="k">if</span> <span class="mi">7</span> <span class="o">&lt;=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span><span class="o">.</span><span class="n">hour</span> <span class="o">&lt;</span> <span class="mi">22</span><span class="p">:</span>
<span class="n">func</span><span class="p">()</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">pass</span> <span class="c1"># Hush, the neighbors are asleep</span>
<span class="k">return</span> <span class="n">wrapper</span>
<span class="k">def</span> <span class="nf">say_whee</span><span class="p">():</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Whee!&quot;</span><span class="p">)</span>
<span class="n">say_whee</span> <span class="o">=</span> <span class="n">not_during_the_night</span><span class="p">(</span><span class="n">say_whee</span><span class="p">)</span>
</code></pre></div>
<p>If you try to call <code>say_whee()</code> after bedtime, nothing will happen:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">say_whee</span><span class="p">()</span>
<span class="go">&gt;&gt;&gt;</span>
</code></pre></div>
<div class="w-100 text-center js-needs-scaling" style="transform-origin: 0 0;"><div id="waldo-tag-5000"></div> <a class="small text-muted js-disclosure" href="/account/join/" rel="nofollow" style="display: none;"> <i aria-hidden="true" class="fa fa-info-circle"> </i> Remove ads</a></div><section class="section3" id="syntactic-sugar"><h3>Syntactic Sugar!<a class="headerlink" href="#syntactic-sugar" title="Permanent link"></a></h3>
<p>The way you decorated <code>say_whee()</code> above is a little clunky. First of all, you end up typing the name <code>say_whee</code> three times. In addition, the decoration gets a bit hidden away below the definition of the function.</p>
<p>Instead, Python allows you to <strong>use decorators in a simpler way with the <code>@</code> symbol</strong>, sometimes called the <a href="https://www.python.org/dev/peps/pep-0318/#background">&ldquo;pie&rdquo; syntax</a>. The following example does the exact same thing as the first decorator example:</p>
<div class="highlight python"><pre><span></span><code><span class="k">def</span> <span class="nf">my_decorator</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="nb">print</span><span class="p">(</span><span class="s2">&quot;Something is happening before the function is called.&quot;</span><span class="p">)</span>
<span class="n">func</span><span class="p">()</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Something is happening after the function is called.&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="n">wrapper</span>
<span class="nd">@my_decorator</span>
<span class="k">def</span> <span class="nf">say_whee</span><span class="p">():</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Whee!&quot;</span><span class="p">)</span>
</code></pre></div>
<p>So, <code>@my_decorator</code> is just an easier way of saying <code>say_whee = my_decorator(say_whee)</code>. It&rsquo;s how you apply a decorator to a function.</p>
</section><section class="section3" id="reusing-decorators"><h3>Reusing Decorators<a class="headerlink" href="#reusing-decorators" title="Permanent link"></a></h3>
<p>Recall that a decorator is just a regular Python function. All the usual tools for easy reusability are available. Let&rsquo;s move the decorator to its own <a href="https://realpython.com/python-modules-packages/">module</a> that can be used in many other functions.</p>
<p>Create a file called <code>decorators.py</code> with the following content:</p>
<div class="highlight python"><pre><span></span><code><span class="k">def</span> <span class="nf">do_twice</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">wrapper_do_twice</span><span class="p">():</span>
<span class="n">func</span><span class="p">()</span>
<span class="n">func</span><span class="p">()</span>
<span class="k">return</span> <span class="n">wrapper_do_twice</span>
</code></pre></div>
<div class="alert alert-primary" role="alert">
<p><strong>Note:</strong> You can name your inner function whatever you want, and a generic name like <code>wrapper()</code> is usually okay. You&rsquo;ll see a lot of decorators in this article. To keep them apart, we&rsquo;ll name the inner function with the same name as the decorator but with a <code>wrapper_</code> prefix.</p>
</div>
<p>You can now use this new decorator in other files by doing a regular <a href="https://realpython.com/absolute-vs-relative-python-imports/">import</a>:</p>
<div class="highlight python"><pre><span></span><code><span class="kn">from</span> <span class="nn">decorators</span> <span class="kn">import</span> <span class="n">do_twice</span>
<span class="nd">@do_twice</span>
<span class="k">def</span> <span class="nf">say_whee</span><span class="p">():</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Whee!&quot;</span><span class="p">)</span>
</code></pre></div>
<p>When you run this example, you should see that the original <code>say_whee()</code> is executed twice:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">say_whee</span><span class="p">()</span>
<span class="go">Whee!</span>
<span class="go">Whee!</span>
</code></pre></div>
<div class="alert alert-warning" role="alert"><p><strong>Free Bonus:</strong> <a href="" class="alert-link" data-toggle="modal" data-target="#modal-power-of-decorators-fixed" data-focus="false">Click here to get access to a free "The Power of Python Decorators" guide</a> that shows you 3 advanced decorator patterns and techniques you can use to write to cleaner and more Pythonic programs.</p></div>
</section><section class="section3" id="decorating-functions-with-arguments"><h3>Decorating Functions With Arguments<a class="headerlink" href="#decorating-functions-with-arguments" title="Permanent link"></a></h3>
<p>Say that you have a function that accepts some arguments. Can you still decorate it? Let&rsquo;s try:</p>
<div class="highlight python"><pre><span></span><code><span class="kn">from</span> <span class="nn">decorators</span> <span class="kn">import</span> <span class="n">do_twice</span>
<span class="nd">@do_twice</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="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Hello </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
</code></pre></div>
<p>Unfortunately, running this code raises an error:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">greet</span><span class="p">(</span><span class="s2">&quot;World&quot;</span><span class="p">)</span>
<span class="gt">Traceback (most recent call last):</span>
File <span class="nb">&quot;&lt;stdin&gt;&quot;</span>, line <span class="m">1</span>, in <span class="n">&lt;module&gt;</span>
<span class="gr">TypeError</span>: <span class="n">wrapper_do_twice() takes 0 positional arguments but 1 was given</span>
</code></pre></div>
<p>The problem is that the inner function <code>wrapper_do_twice()</code> does not take any arguments, but <code>name="World"</code> was passed to it. You could fix this by letting <code>wrapper_do_twice()</code> accept one argument, but then it would not work for the <code>say_whee()</code> function you created earlier.</p>
<p>The solution is to use <a href="https://realpython.com/python-kwargs-and-args/"><code>*args</code> and <code>**kwargs</code></a> in the inner wrapper function. Then it will accept an arbitrary number of positional and keyword arguments. Rewrite <code>decorators.py</code> as follows:</p>
<div class="highlight python"><pre><span></span><code><span class="k">def</span> <span class="nf">do_twice</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
<span class="hll"> <span class="k">def</span> <span class="nf">wrapper_do_twice</span><span class="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><span class="hll"> <span class="n">func</span><span class="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><span class="hll"> <span class="n">func</span><span class="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> <span class="k">return</span> <span class="n">wrapper_do_twice</span>
</code></pre></div>
<p>The <code>wrapper_do_twice()</code> inner function now accepts any number of arguments and passes them on to the function it decorates. Now both your <code>say_whee()</code> and <code>greet()</code> examples works:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">say_whee</span><span class="p">()</span>
<span class="go">Whee!</span>
<span class="go">Whee!</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">greet</span><span class="p">(</span><span class="s2">&quot;World&quot;</span><span class="p">)</span>
<span class="go">Hello World</span>
<span class="go">Hello World</span>
</code></pre></div>
<div class="w-100 text-center js-needs-scaling" style="transform-origin: 0 0;"><div id="waldo-tag-5002"></div> <a class="small text-muted js-disclosure" href="/account/join/" rel="nofollow" style="display: none;"> <i aria-hidden="true" class="fa fa-info-circle"> </i> Remove ads</a></div></section><section class="section3" id="returning-values-from-decorated-functions"><h3>Returning Values From Decorated Functions<a class="headerlink" href="#returning-values-from-decorated-functions" title="Permanent link"></a></h3>
<p>What happens to the return value of decorated functions? Well, that&rsquo;s up to the decorator to decide. Let&rsquo;s say you decorate a simple function as follows:</p>
<div class="highlight python"><pre><span></span><code><span class="kn">from</span> <span class="nn">decorators</span> <span class="kn">import</span> <span class="n">do_twice</span>
<span class="nd">@do_twice</span>
<span class="k">def</span> <span class="nf">return_greeting</span><span class="p">(</span><span class="n">name</span><span class="p">):</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Creating greeting&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">&quot;Hi </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">&quot;</span>
</code></pre></div>
<p>Try to use it:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">hi_adam</span> <span class="o">=</span> <span class="n">return_greeting</span><span class="p">(</span><span class="s2">&quot;Adam&quot;</span><span class="p">)</span>
<span class="go">Creating greeting</span>
<span class="go">Creating greeting</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">print</span><span class="p">(</span><span class="n">hi_adam</span><span class="p">)</span>
<span class="go">None</span>
</code></pre></div>
<p>Oops, your decorator ate the return value from the function.</p>
<p>Because the <code>do_twice_wrapper()</code> doesn&rsquo;t explicitly return a value, the call <code>return_greeting("Adam")</code> ended up returning <code>None</code>.</p>
<p>To fix this, you need to <strong>make sure the wrapper function returns the return value of the decorated function</strong>. Change your <code>decorators.py</code> file:</p>
<div class="highlight python"><pre><span></span><code><span class="k">def</span> <span class="nf">do_twice</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">wrapper_do_twice</span><span class="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">func</span><span class="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="hll"> <span class="k">return</span> <span class="n">func</span><span class="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> <span class="k">return</span> <span class="n">wrapper_do_twice</span>
</code></pre></div>
<p>The return value from the last execution of the function is returned:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">return_greeting</span><span class="p">(</span><span class="s2">&quot;Adam&quot;</span><span class="p">)</span>
<span class="go">Creating greeting</span>
<span class="go">Creating greeting</span>
<span class="go">&#39;Hi Adam&#39;</span>
</code></pre></div>
</section><section class="section3" id="who-are-you-really"><h3>Who Are You, Really?<a class="headerlink" href="#who-are-you-really" title="Permanent link"></a></h3>
<p>A great convenience when working with Python, especially in the interactive shell, is its powerful introspection ability. <a href="https://en.wikipedia.org/wiki/Type_introspection">Introspection</a> is the ability of an object to know about its own attributes at runtime. For instance, a function knows its own name and <a href="https://realpython.com/documenting-python-code/">documentation</a>:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="nb">print</span>
<span class="go">&lt;built-in function print&gt;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">print</span><span class="o">.</span><span class="vm">__name__</span>
<span class="go">&#39;print&#39;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">help</span><span class="p">(</span><span class="nb">print</span><span class="p">)</span>
<span class="go">Help on built-in function print in module builtins:</span>
<span class="go">print(...)</span>
<span class="go"> &lt;full help message&gt;</span>
</code></pre></div>
<p>The introspection works for functions you define yourself as well:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">say_whee</span>
<span class="go">&lt;function do_twice.&lt;locals&gt;.wrapper_do_twice at 0x7f43700e52f0&gt;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">say_whee</span><span class="o">.</span><span class="vm">__name__</span>
<span class="go">&#39;wrapper_do_twice&#39;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">help</span><span class="p">(</span><span class="n">say_whee</span><span class="p">)</span>
<span class="go">Help on function wrapper_do_twice in module decorators:</span>
<span class="go">wrapper_do_twice()</span>
</code></pre></div>
<p>However, after being decorated, <code>say_whee()</code> has gotten very confused about its identity. It now reports being the <code>wrapper_do_twice()</code> inner function inside the <code>do_twice()</code> decorator. Although technically true, this is not very useful information.</p>
<p>To fix this, decorators should use the <a href="https://docs.python.org/library/functools.html#functools.wraps"><code>@functools.wraps</code></a> decorator, which will preserve information about the original function. Update <code>decorators.py</code> again:</p>
<div class="highlight python"><pre><span></span><code><span class="hll"><span class="kn">import</span> <span class="nn">functools</span>
</span>
<span class="k">def</span> <span class="nf">do_twice</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
<span class="hll"> <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> <span class="k">def</span> <span class="nf">wrapper_do_twice</span><span class="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">func</span><span class="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="k">return</span> <span class="n">func</span><span class="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="k">return</span> <span class="n">wrapper_do_twice</span>
</code></pre></div>
<p>You do not need to change anything about the decorated <code>say_whee()</code> function:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">say_whee</span>
<span class="go">&lt;function say_whee at 0x7ff79a60f2f0&gt;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">say_whee</span><span class="o">.</span><span class="vm">__name__</span>
<span class="go">&#39;say_whee&#39;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">help</span><span class="p">(</span><span class="n">say_whee</span><span class="p">)</span>
<span class="go">Help on function say_whee in module whee:</span>
<span class="go">say_whee()</span>
</code></pre></div>
<p>Much better! Now <code>say_whee()</code> is still itself after decoration.</p>
<div class="alert alert-primary" role="alert">
<p><strong>Technical Detail:</strong> The <code>@functools.wraps</code> decorator <a href="https://github.com/python/cpython/blob/5d4cb54800966947db2e86f65fb109c5067076be/Lib/functools.py#L34">uses</a> the function <code>functools.update_wrapper()</code> to update special attributes like <code>__name__</code> and <code>__doc__</code> that are used in the introspection.</p>
</div>
<div class="w-100 text-center js-needs-scaling" style="transform-origin: 0 0;"><div id="waldo-tag-5004"></div> <a class="small text-muted js-disclosure" href="/account/join/" rel="nofollow" style="display: none;"> <i aria-hidden="true" class="fa fa-info-circle"> </i> Remove ads</a></div></section></section><section class="section2" id="a-few-real-world-examples"><h2>A Few Real World Examples<a class="headerlink" href="#a-few-real-world-examples" title="Permanent link"></a></h2>
<p>Let&rsquo;s look at a few more useful examples of decorators. You&rsquo;ll notice that they&rsquo;ll mainly follow the same pattern that you&rsquo;ve learned so far:</p>
<div class="highlight python"><pre><span></span><code><span class="kn">import</span> <span class="nn">functools</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="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_decorator</span><span class="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="c1"># Do something before</span>
<span class="n">value</span> <span class="o">=</span> <span class="n">func</span><span class="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="c1"># Do something after</span>
<span class="k">return</span> <span class="n">value</span>
<span class="k">return</span> <span class="n">wrapper_decorator</span>
</code></pre></div>
<p>This formula is a good boilerplate template for building more complex decorators.</p>
<div class="alert alert-primary" role="alert">
<p><strong>Note:</strong> In later examples, we will assume that these decorators are saved in your <code>decorators.py</code> file as well. Recall that you can download <a href="https://github.com/realpython/materials/tree/master/primer-on-python-decorators">all the examples in this tutorial</a>.</p>
</div>
<section class="section3" id="timing-functions"><h3>Timing Functions<a class="headerlink" href="#timing-functions" title="Permanent link"></a></h3>
<p>Let&rsquo;s start by creating a <code>@timer</code> decorator. It will <a href="https://realpython.com/python-timer/">measure the time a function takes to execute</a> and print the duration to the console. Here&rsquo;s the code:</p>
<div class="highlight python"><pre><span></span><code><span class="kn">import</span> <span class="nn">functools</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="k">def</span> <span class="nf">timer</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Print the runtime of the decorated function&quot;&quot;&quot;</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_timer</span><span class="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">start_time</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">perf_counter</span><span class="p">()</span> <span class="c1"># 1</span>
<span class="n">value</span> <span class="o">=</span> <span class="n">func</span><span class="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">end_time</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">perf_counter</span><span class="p">()</span> <span class="c1"># 2</span>
<span class="n">run_time</span> <span class="o">=</span> <span class="n">end_time</span> <span class="o">-</span> <span class="n">start_time</span> <span class="c1"># 3</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Finished </span><span class="si">{</span><span class="n">func</span><span class="o">.</span><span class="vm">__name__</span><span class="si">!r}</span><span class="s2"> in </span><span class="si">{</span><span class="n">run_time</span><span class="si">:</span><span class="s2">.4f</span><span class="si">}</span><span class="s2"> secs&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="n">value</span>
<span class="k">return</span> <span class="n">wrapper_timer</span>
<span class="nd">@timer</span>
<span class="k">def</span> <span class="nf">waste_some_time</span><span class="p">(</span><span class="n">num_times</span><span class="p">):</span>
<span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">num_times</span><span class="p">):</span>
<span class="nb">sum</span><span class="p">([</span><span class="n">i</span><span class="o">**</span><span class="mi">2</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">10000</span><span class="p">)])</span>
</code></pre></div>
<p>This decorator works by storing the time just before the function starts running (at the line marked <code># 1</code>) and just after the function finishes (at <code># 2</code>). The time the function takes is then the difference between the two (at <code># 3</code>). We use the <a href="https://docs.python.org/library/time.html#time.perf_counter"><code>time.perf_counter()</code></a> function, which does a good job of measuring time intervals. Here are some examples of timings:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">waste_some_time</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="go">Finished &#39;waste_some_time&#39; in 0.0010 secs</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">waste_some_time</span><span class="p">(</span><span class="mi">999</span><span class="p">)</span>
<span class="go">Finished &#39;waste_some_time&#39; in 0.3260 secs</span>
</code></pre></div>
<p>Run it yourself. Work through the code line by line. Make sure you understand how it works. Don&rsquo;t worry if you don&rsquo;t get it, though. Decorators are advanced beings. Try to sleep on it or make a drawing of the program flow.</p>
<div class="alert alert-primary" role="alert">
<p><strong>Note:</strong> The <code>@timer</code> decorator is great if you just want to get an idea about the runtime of your functions. If you want to do more precise measurements of code, you should instead consider the <a href="https://docs.python.org/library/timeit.html"><code>timeit</code> module</a> in the standard library. It temporarily disables <a href="https://realpython.com/python-memory-management/#garbage-collection">garbage collection</a> and runs multiple trials to strip out noise from quick function calls.</p>
</div>
</section><section class="section3" id="debugging-code"><h3>Debugging Code<a class="headerlink" href="#debugging-code" title="Permanent link"></a></h3>
<p>The following <code>@debug</code> decorator will print the arguments a function is called with as well as its return value every time the function is called:</p>
<div class="highlight python"><pre><span></span><code><span class="kn">import</span> <span class="nn">functools</span>
<span class="k">def</span> <span class="nf">debug</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Print the function signature and return value&quot;&quot;&quot;</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_debug</span><span class="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">args_repr</span> <span class="o">=</span> <span class="p">[</span><span class="nb">repr</span><span class="p">(</span><span class="n">a</span><span class="p">)</span> <span class="k">for</span> <span class="n">a</span> <span class="ow">in</span> <span class="n">args</span><span class="p">]</span> <span class="c1"># 1</span>
<span class="n">kwargs_repr</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">k</span><span class="si">}</span><span class="s2">=</span><span class="si">{</span><span class="n">v</span><span class="si">!r}</span><span class="s2">&quot;</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">kwargs</span><span class="o">.</span><span class="n">items</span><span class="p">()]</span> <span class="c1"># 2</span>
<span class="n">signature</span> <span class="o">=</span> <span class="s2">&quot;, &quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">args_repr</span> <span class="o">+</span> <span class="n">kwargs_repr</span><span class="p">)</span> <span class="c1"># 3</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Calling </span><span class="si">{</span><span class="n">func</span><span class="o">.</span><span class="vm">__name__</span><span class="si">}</span><span class="s2">(</span><span class="si">{</span><span class="n">signature</span><span class="si">}</span><span class="s2">)&quot;</span><span class="p">)</span>
<span class="n">value</span> <span class="o">=</span> <span class="n">func</span><span class="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="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">func</span><span class="o">.</span><span class="vm">__name__</span><span class="si">!r}</span><span class="s2"> returned </span><span class="si">{</span><span class="n">value</span><span class="si">!r}</span><span class="s2">&quot;</span><span class="p">)</span> <span class="c1"># 4</span>
<span class="k">return</span> <span class="n">value</span>
<span class="k">return</span> <span class="n">wrapper_debug</span>
</code></pre></div>
<p>The signature is created by joining the <a href="https://dbader.org/blog/python-repr-vs-str">string representations</a> of all the arguments. The numbers in the following list correspond to the numbered comments in the code:</p>
<ol>
<li>Create a list of the positional arguments. Use <code>repr()</code> to get a nice string representing each argument.</li>
<li>Create a list of the keyword arguments. The <a href="https://realpython.com/python-f-strings/">f-string</a> formats each argument as <code>key=value</code> where the <code>!r</code> specifier means that <code>repr()</code> is used to represent the value.</li>
<li>The lists of positional and keyword arguments is joined together to one signature string with each argument separated by a comma.</li>
<li>The return value is printed after the function is executed.</li>
</ol>
<p>Let&rsquo;s see how the decorator works in practice by applying it to a simple function with one position and one keyword argument:</p>
<div class="highlight python"><pre><span></span><code><span class="nd">@debug</span>
<span class="k">def</span> <span class="nf">make_greeting</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">age</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="k">if</span> <span class="n">age</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">&quot;Howdy </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">!&quot;</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">&quot;Whoa </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">age</span><span class="si">}</span><span class="s2"> already, you are growing up!&quot;</span>
</code></pre></div>
<p>Note how the <code>@debug</code> decorator prints the signature and return value of the <code>make_greeting()</code> function:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">make_greeting</span><span class="p">(</span><span class="s2">&quot;Benjamin&quot;</span><span class="p">)</span>
<span class="go">Calling make_greeting(&#39;Benjamin&#39;)</span>
<span class="go">&#39;make_greeting&#39; returned &#39;Howdy Benjamin!&#39;</span>
<span class="go">&#39;Howdy Benjamin!&#39;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">make_greeting</span><span class="p">(</span><span class="s2">&quot;Richard&quot;</span><span class="p">,</span> <span class="n">age</span><span class="o">=</span><span class="mi">112</span><span class="p">)</span>
<span class="go">Calling make_greeting(&#39;Richard&#39;, age=112)</span>
<span class="go">&#39;make_greeting&#39; returned &#39;Whoa Richard! 112 already, you are growing up!&#39;</span>
<span class="go">&#39;Whoa Richard! 112 already, you are growing up!&#39;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">make_greeting</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s2">&quot;Dorrisile&quot;</span><span class="p">,</span> <span class="n">age</span><span class="o">=</span><span class="mi">116</span><span class="p">)</span>
<span class="go">Calling make_greeting(name=&#39;Dorrisile&#39;, age=116)</span>
<span class="go">&#39;make_greeting&#39; returned &#39;Whoa Dorrisile! 116 already, you are growing up!&#39;</span>
<span class="go">&#39;Whoa Dorrisile! 116 already, you are growing up!&#39;</span>
</code></pre></div>
<p>This example might not seem immediately useful since the <code>@debug</code> decorator just repeats what you just wrote. It&rsquo;s more powerful when applied to small convenience functions that you don&rsquo;t call directly yourself.</p>
<p>The following example calculates an approximation to the <a href="https://en.wikipedia.org/wiki/E_(mathematical_constant)">mathematical constant <em>e</em></a>:</p>
<div class="highlight python"><pre><span></span><code><span class="kn">import</span> <span class="nn">math</span>
<span class="kn">from</span> <span class="nn">decorators</span> <span class="kn">import</span> <span class="n">debug</span>
<span class="c1"># Apply a decorator to a standard library function</span>
<span class="n">math</span><span class="o">.</span><span class="n">factorial</span> <span class="o">=</span> <span class="n">debug</span><span class="p">(</span><span class="n">math</span><span class="o">.</span><span class="n">factorial</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">approximate_e</span><span class="p">(</span><span class="n">terms</span><span class="o">=</span><span class="mi">18</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">sum</span><span class="p">(</span><span class="mi">1</span> <span class="o">/</span> <span class="n">math</span><span class="o">.</span><span class="n">factorial</span><span class="p">(</span><span class="n">n</span><span class="p">)</span> <span class="k">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">terms</span><span class="p">))</span>
</code></pre></div>
<p>This example also shows how you can apply a decorator to a function that has already been defined. The approximation of <em>e</em> is based on the following <a href="https://en.wikipedia.org/wiki/E_(mathematical_constant)">series expansion</a>:</p>
<figure><a href="https://files.realpython.com/media/e_series_long.7ce8d6492b4f.png" target="_blank"><img loading="lazy" class="img-fluid mx-auto d-block w-66" src="https://files.realpython.com/media/e_series_long.7ce8d6492b4f.png" width="1935" height="228" srcset="https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/e_series_long.7ce8d6492b4f.png&amp;w=483&amp;sig=78370c9bf4724332d985cae21d5c7ed5de9f5397 483w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/e_series_long.7ce8d6492b4f.png&amp;w=967&amp;sig=13a780a5e9682c6ac8ee58960d9d82bb0d01284e 967w, https://files.realpython.com/media/e_series_long.7ce8d6492b4f.png 1935w" sizes="75vw" alt="Series for calculating mathematical constant e" data-asset="636" /></a></figure>
<p>When calling the <code>approximate_e()</code> function, you can see the <code>@debug</code> decorator at work:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">approximate_e</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
<span class="go">Calling factorial(0)</span>
<span class="go">&#39;factorial&#39; returned 1</span>
<span class="go">Calling factorial(1)</span>
<span class="go">&#39;factorial&#39; returned 1</span>
<span class="go">Calling factorial(2)</span>
<span class="go">&#39;factorial&#39; returned 2</span>
<span class="go">Calling factorial(3)</span>
<span class="go">&#39;factorial&#39; returned 6</span>
<span class="go">Calling factorial(4)</span>
<span class="go">&#39;factorial&#39; returned 24</span>
<span class="go">2.708333333333333</span>
</code></pre></div>
<p>In this example, you get a decent approximation to the true value <em>e</em> = 2.718281828, adding only 5 terms.</p>
<div class="w-100 text-center js-needs-scaling" style="transform-origin: 0 0;"><div id="waldo-tag-5257"></div> <a class="small text-muted js-disclosure" href="/account/join/" rel="nofollow" style="display: none;"> <i aria-hidden="true" class="fa fa-info-circle"> </i> Remove ads</a></div></section><section class="section3" id="slowing-down-code"><h3>Slowing Down Code<a class="headerlink" href="#slowing-down-code" title="Permanent link"></a></h3>
<p>This next example might not seem very useful. Why would you want to slow down your Python code? Probably the most common use case is that you want to rate-limit a function that continuously checks whether a resource&mdash;like a web page&mdash;has changed. The <code>@slow_down</code> decorator will sleep one second before it calls the decorated function:</p>
<div class="highlight python"><pre><span></span><code><span class="kn">import</span> <span class="nn">functools</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="k">def</span> <span class="nf">slow_down</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Sleep 1 second before calling the function&quot;&quot;&quot;</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_slow_down</span><span class="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">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="k">return</span> <span class="n">func</span><span class="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="k">return</span> <span class="n">wrapper_slow_down</span>
<span class="nd">@slow_down</span>
<span class="k">def</span> <span class="nf">countdown</span><span class="p">(</span><span class="n">from_number</span><span class="p">):</span>
<span class="k">if</span> <span class="n">from_number</span> <span class="o">&lt;</span> <span class="mi">1</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Liftoff!&quot;</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="n">from_number</span><span class="p">)</span>
<span class="n">countdown</span><span class="p">(</span><span class="n">from_number</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
</code></pre></div>
<p>To see the effect of the <code>@slow_down</code> decorator, you really need to run the example yourself:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">countdown</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
<span class="go">3</span>
<span class="go">2</span>
<span class="go">1</span>
<span class="go">Liftoff!</span>
</code></pre></div>
<div class="alert alert-primary" role="alert">
<p><strong>Note:</strong> The <code>countdown()</code> function is a recursive function. In other words, it&rsquo;s a function calling itself. To learn more about recursive functions in Python, see our guide on <a href="https://realpython.com/python-thinking-recursively/">Thinking Recursively in Python</a>.</p>
</div>
<p>The <code>@slow_down</code> decorator always sleeps for one second. <a href="#slowing-down-code-revisited">Later</a>, you&rsquo;ll see how to control the rate by passing an argument to the decorator.</p>
</section><section class="section3" id="registering-plugins"><h3>Registering Plugins<a class="headerlink" href="#registering-plugins" title="Permanent link"></a></h3>
<p>Decorators don&rsquo;t have to wrap the function they&rsquo;re decorating. They can also simply register that a function exists and return it unwrapped. This can be used, for instance, to create a light-weight plug-in architecture:</p>
<div class="highlight python"><pre><span></span><code><span class="kn">import</span> <span class="nn">random</span>
<span class="n">PLUGINS</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">register</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Register a function as a plug-in&quot;&quot;&quot;</span>
<span class="n">PLUGINS</span><span class="p">[</span><span class="n">func</span><span class="o">.</span><span class="vm">__name__</span><span class="p">]</span> <span class="o">=</span> <span class="n">func</span>
<span class="k">return</span> <span class="n">func</span>
<span class="nd">@register</span>
<span class="k">def</span> <span class="nf">say_hello</span><span class="p">(</span><span class="n">name</span><span class="p">):</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">&quot;Hello </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="nd">@register</span>
<span class="k">def</span> <span class="nf">be_awesome</span><span class="p">(</span><span class="n">name</span><span class="p">):</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">&quot;Yo </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">, together we are the awesomest!&quot;</span>
<span class="k">def</span> <span class="nf">randomly_greet</span><span class="p">(</span><span class="n">name</span><span class="p">):</span>
<span class="n">greeter</span><span class="p">,</span> <span class="n">greeter_func</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="n">PLUGINS</span><span class="o">.</span><span class="n">items</span><span class="p">()))</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Using </span><span class="si">{</span><span class="n">greeter</span><span class="si">!r}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="n">greeter_func</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
</code></pre></div>
<p>The <code>@register</code> decorator simply stores a reference to the decorated function in the global <code>PLUGINS</code> dict. Note that you do not have to write an inner function or use <code>@functools.wraps</code> in this example because you are returning the original function unmodified.</p>
<p>The <code>randomly_greet()</code> function randomly chooses one of the registered functions to use. Note that the <code>PLUGINS</code> dictionary already contains references to each function object that is registered as a plugin:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">PLUGINS</span>
<span class="go">{&#39;say_hello&#39;: &lt;function say_hello at 0x7f768eae6730&gt;,</span>
<span class="go"> &#39;be_awesome&#39;: &lt;function be_awesome at 0x7f768eae67b8&gt;}</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">randomly_greet</span><span class="p">(</span><span class="s2">&quot;Alice&quot;</span><span class="p">)</span>
<span class="go">Using &#39;say_hello&#39;</span>
<span class="go">&#39;Hello Alice&#39;</span>
</code></pre></div>
<p>The main benefit of this simple plugin architecture is that you do not need to maintain a list of which plugins exist. That list is created when the plugins register themselves. This makes it trivial to add a new plugin: just define the function and decorate it with <code>@register</code>.</p>
<p>If you are familiar with <code>globals()</code> in Python, you might see some similarities to how the plugin architecture works. <code>globals()</code> gives access to all global variables in the current scope, including your plugins:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="nb">globals</span><span class="p">()</span>
<span class="go">{..., # Lots of variables not shown here.</span>
<span class="go"> &#39;say_hello&#39;: &lt;function say_hello at 0x7f768eae6730&gt;,</span>
<span class="go"> &#39;be_awesome&#39;: &lt;function be_awesome at 0x7f768eae67b8&gt;,</span>
<span class="go"> &#39;randomly_greet&#39;: &lt;function randomly_greet at 0x7f768eae6840&gt;}</span>
</code></pre></div>
<p>Using the <code>@register</code> decorator, you can create your own curated list of interesting variables, effectively hand-picking some functions from <code>globals()</code>.</p>
</section><section class="section3" id="is-the-user-logged-in"><h3>Is the User Logged In?<a class="headerlink" href="#is-the-user-logged-in" title="Permanent link"></a></h3>
<p>The final example before moving on to some fancier decorators is commonly used when working with a web framework. In this example, we are using <a href="https://realpython.com/tutorials/flask/">Flask</a> to set up a <code>/secret</code> web page that should only be visible to users that are logged in or otherwise authenticated:</p>
<div class="highlight python"><pre><span></span><code><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span><span class="p">,</span> <span class="n">g</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">redirect</span><span class="p">,</span> <span class="n">url_for</span>
<span class="kn">import</span> <span class="nn">functools</span>
<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="vm">__name__</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">login_required</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Make sure user is logged in before proceeding&quot;&quot;&quot;</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_login_required</span><span class="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="k">if</span> <span class="n">g</span><span class="o">.</span><span class="n">user</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">return</span> <span class="n">redirect</span><span class="p">(</span><span class="n">url_for</span><span class="p">(</span><span class="s2">&quot;login&quot;</span><span class="p">,</span> <span class="nb">next</span><span class="o">=</span><span class="n">request</span><span class="o">.</span><span class="n">url</span><span class="p">))</span>
<span class="k">return</span> <span class="n">func</span><span class="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="k">return</span> <span class="n">wrapper_login_required</span>
<span class="nd">@app</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/secret&quot;</span><span class="p">)</span>
<span class="nd">@login_required</span>
<span class="k">def</span> <span class="nf">secret</span><span class="p">():</span>
<span class="o">...</span>
</code></pre></div>
<p>While this gives an idea about how to add authentication to your web framework, you should usually not write these types of decorators yourself. For Flask, you can use <a href="https://flask-login.readthedocs.io/en/latest/#flask_login.login_required">the Flask-Login extension</a> instead, which adds more security and functionality.</p>
<div class="w-100 text-center js-needs-scaling" style="transform-origin: 0 0;"><div id="waldo-tag-5259"></div> <a class="small text-muted js-disclosure" href="/account/join/" rel="nofollow" style="display: none;"> <i aria-hidden="true" class="fa fa-info-circle"> </i> Remove ads</a></div></section></section><section class="section2" id="fancy-decorators"><h2>Fancy Decorators<a class="headerlink" href="#fancy-decorators" title="Permanent link"></a></h2>
<p>So far, you&rsquo;ve seen how to create simple decorators. You already have a pretty good understanding of what decorators are and how they work. Feel free to take a break from this article to practice everything you&rsquo;ve learned.</p>
<p>In the second part of this tutorial, we&rsquo;ll explore more advanced features, including how to use the following:</p>
<ul>
<li><a href="#decorating-classes">Decorators on classes</a></li>
<li><a href="#nesting-decorators">Several decorators on one function</a></li>
<li><a href="#decorators-with-arguments">Decorators with arguments</a></li>
<li><a href="#both-please-but-never-mind-the-bread">Decorators that can optionally take arguments</a></li>
<li><a href="#stateful-decorators">Stateful decorators</a></li>
<li><a href="#classes-as-decorators">Classes as decorators</a></li>
</ul>
<section class="section3" id="decorating-classes"><h3>Decorating Classes<a class="headerlink" href="#decorating-classes" title="Permanent link"></a></h3>
<p>There are two different ways you can use decorators on classes. The first one is very close to what you have already done with functions: you can <strong>decorate the methods of a class</strong>. This was <a href="https://www.python.org/dev/peps/pep-0318/#motivation">one of the motivations</a> for introducing decorators back in the day.</p>
<p>Some commonly used decorators that are even built-ins in Python are <a href="https://realpython.com/instance-class-and-static-methods-demystified/"><code>@classmethod</code>, <code>@staticmethod</code></a>, and <a href="https://docs.python.org/library/functions.html#property"><code>@property</code></a>. The <code>@classmethod</code> and <code>@staticmethod</code> decorators are used to define methods inside a class <a href="https://realpython.com/python-namespaces-scope/">namespace</a> that are not connected to a particular instance of that class. The <code>@property</code> decorator is used to customize <a href="https://docs.python.org/howto/descriptor.html#properties">getters and setters</a> for class attributes. Expand the box below for an example using these decorators.</p>
<div class="card mb-3" id="collapse_card0584b0">
<div class="card-header border-0">
<p class="m-0"><button class="btn w-100" data-toggle="collapse" data-target="#collapse0584b0" aria-expanded="false" aria-controls="collapse0584b0" markdown="1"><span class="float-left" markdown="1">Example using built-in class decorators</span><span class="float-right text-muted">Show/Hide</span></button></p>
</div>
<div class="collapse" data-parent="#collapse_card0584b0" id="collapse0584b0">
<div class="card-body">
<p>The following definition of a <code>Circle</code> class uses the <code>@classmethod</code>, <code>@staticmethod</code>, and <code>@property</code> decorators:</p>
<div class="highlight python"><pre><span></span><code><span class="k">class</span> <span class="nc">Circle</span><span class="p">:</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">radius</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_radius</span> <span class="o">=</span> <span class="n">radius</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">radius</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Get value of radius&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_radius</span>
<span class="nd">@radius</span><span class="o">.</span><span class="n">setter</span>
<span class="k">def</span> <span class="nf">radius</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Set radius, raise error if negative&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="n">value</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_radius</span> <span class="o">=</span> <span class="n">value</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">&quot;Radius must be positive&quot;</span><span class="p">)</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">area</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Calculate area inside circle&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">pi</span><span class="p">()</span> <span class="o">*</span> <span class="bp">self</span><span class="o">.</span><span class="n">radius</span><span class="o">**</span><span class="mi">2</span>
<span class="k">def</span> <span class="nf">cylinder_volume</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">height</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Calculate volume of cylinder with circle as base&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">area</span> <span class="o">*</span> <span class="n">height</span>
<span class="nd">@classmethod</span>
<span class="k">def</span> <span class="nf">unit_circle</span><span class="p">(</span><span class="bp">cls</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Factory method creating a circle with radius 1&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">cls</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="nd">@staticmethod</span>
<span class="k">def</span> <span class="nf">pi</span><span class="p">():</span>
<span class="sd">&quot;&quot;&quot;Value of π, could use math.pi instead though&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="mf">3.1415926535</span>
</code></pre></div>
<p>In this class:</p>
<ul>
<li><code>.cylinder_volume()</code> is a regular method.</li>
<li><code>.radius</code> is a mutable property: it can be set to a different value. However, by defining a setter method, we can do some error testing to make sure it&rsquo;s not set to a nonsensical negative number. Properties are accessed as attributes without parentheses.</li>
<li><code>.area</code> is an immutable property: properties without <code>.setter()</code> methods can&rsquo;t be changed. Even though it is defined as a method, it can be retrieved as an attribute without parentheses.</li>
<li><code>.unit_circle()</code> is a class method. It&rsquo;s not bound to one particular instance of <code>Circle</code>. Class methods are often used as factory methods that can create specific instances of the class.</li>
<li><code>.pi()</code> is a static method. It&rsquo;s not really dependent on the <code>Circle</code> class, except that it is part of its namespace. Static methods can be called on either an instance or the class.</li>
</ul>
<p>The <code>Circle</code> class can for example be used as follows:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">c</span> <span class="o">=</span> <span class="n">Circle</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">c</span><span class="o">.</span><span class="n">radius</span>
<span class="go">5</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">c</span><span class="o">.</span><span class="n">area</span>
<span class="go">78.5398163375</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">c</span><span class="o">.</span><span class="n">radius</span> <span class="o">=</span> <span class="mi">2</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">c</span><span class="o">.</span><span class="n">area</span>
<span class="go">12.566370614</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">c</span><span class="o">.</span><span class="n">area</span> <span class="o">=</span> <span class="mi">100</span>
<span class="go">AttributeError: can&#39;t set attribute</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">c</span><span class="o">.</span><span class="n">cylinder_volume</span><span class="p">(</span><span class="n">height</span><span class="o">=</span><span class="mi">4</span><span class="p">)</span>
<span class="go">50.265482456</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">c</span><span class="o">.</span><span class="n">radius</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span>
<span class="go">ValueError: Radius must be positive</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">c</span> <span class="o">=</span> <span class="n">Circle</span><span class="o">.</span><span class="n">unit_circle</span><span class="p">()</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">c</span><span class="o">.</span><span class="n">radius</span>
<span class="go">1</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">c</span><span class="o">.</span><span class="n">pi</span><span class="p">()</span>
<span class="go">3.1415926535</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">Circle</span><span class="o">.</span><span class="n">pi</span><span class="p">()</span>
<span class="go">3.1415926535</span>
</code></pre></div>
</div>
</div>
</div>
<p>Let&rsquo;s define a class where we decorate some of its methods using the <a href="#debugging-code"><code>@debug</code></a> and <a href="#timing-functions"><code>@timer</code></a> decorators from <a href="#a-few-real-world-examples">earlier</a>:</p>
<div class="highlight python"><pre><span></span><code><span class="kn">from</span> <span class="nn">decorators</span> <span class="kn">import</span> <span class="n">debug</span><span class="p">,</span> <span class="n">timer</span>
<span class="k">class</span> <span class="nc">TimeWaster</span><span class="p">:</span>
<span class="nd">@debug</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">max_num</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">max_num</span> <span class="o">=</span> <span class="n">max_num</span>
<span class="nd">@timer</span>
<span class="k">def</span> <span class="nf">waste_time</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">num_times</span><span class="p">):</span>
<span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">num_times</span><span class="p">):</span>
<span class="nb">sum</span><span class="p">([</span><span class="n">i</span><span class="o">**</span><span class="mi">2</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">max_num</span><span class="p">)])</span>
</code></pre></div>
<p>Using this class, you can see the effect of the decorators:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">tw</span> <span class="o">=</span> <span class="n">TimeWaster</span><span class="p">(</span><span class="mi">1000</span><span class="p">)</span>
<span class="go">Calling __init__(&lt;time_waster.TimeWaster object at 0x7efccce03908&gt;, 1000)</span>
<span class="go">&#39;__init__&#39; returned None</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">tw</span><span class="o">.</span><span class="n">waste_time</span><span class="p">(</span><span class="mi">999</span><span class="p">)</span>
<span class="go">Finished &#39;waste_time&#39; in 0.3376 secs</span>
</code></pre></div>
<p>The other way to use decorators on classes is to <strong>decorate the whole class</strong>. This is, for example, done in the new <a href="https://realpython.com/python-data-classes/"><code>dataclasses</code> module</a> in <a href="https://realpython.com/python37-new-features/">Python 3.7</a>:</p>
<div class="highlight python"><pre><span></span><code><span class="kn">from</span> <span class="nn">dataclasses</span> <span class="kn">import</span> <span class="n">dataclass</span>
<span class="nd">@dataclass</span>
<span class="k">class</span> <span class="nc">PlayingCard</span><span class="p">:</span>
<span class="n">rank</span><span class="p">:</span> <span class="nb">str</span>
<span class="n">suit</span><span class="p">:</span> <span class="nb">str</span>
</code></pre></div>
<p>The meaning of the syntax is similar to the function decorators. In the example above, you could have done the decoration by writing <code>PlayingCard = dataclass(PlayingCard)</code>.</p>
<p>A <a href="https://www.python.org/dev/peps/pep-3129/#rationale">common use of class decorators</a> is to be a simpler alternative to some use-cases of <a href="https://realpython.com/python-metaclasses/">metaclasses</a>. In both cases, you are changing the definition of a class dynamically.</p>
<p>Writing a class decorator is very similar to writing a function decorator. The only difference is that the decorator will receive a class and not a function as an argument. In fact, all the decorators <a href="#a-few-real-world-examples">you saw above</a> will work as class decorators. When you are using them on a class instead of a function, their effect might not be what you want. In the following example, the <code>@timer</code> decorator is applied to a class:</p>
<div class="highlight python"><pre><span></span><code><span class="kn">from</span> <span class="nn">decorators</span> <span class="kn">import</span> <span class="n">timer</span>
<span class="nd">@timer</span>
<span class="k">class</span> <span class="nc">TimeWaster</span><span class="p">:</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">max_num</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">max_num</span> <span class="o">=</span> <span class="n">max_num</span>
<span class="k">def</span> <span class="nf">waste_time</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">num_times</span><span class="p">):</span>
<span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">num_times</span><span class="p">):</span>
<span class="nb">sum</span><span class="p">([</span><span class="n">i</span><span class="o">**</span><span class="mi">2</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">max_num</span><span class="p">)])</span>
</code></pre></div>
<p>Decorating a class does not decorate its methods. Recall that <code>@timer</code> is just shorthand for <code>TimeWaster = timer(TimeWaster)</code>.</p>
<p>Here, <code>@timer</code> only measures the time it takes to instantiate the class:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">tw</span> <span class="o">=</span> <span class="n">TimeWaster</span><span class="p">(</span><span class="mi">1000</span><span class="p">)</span>
<span class="go">Finished &#39;TimeWaster&#39; in 0.0000 secs</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">tw</span><span class="o">.</span><span class="n">waste_time</span><span class="p">(</span><span class="mi">999</span><span class="p">)</span>
<span class="go">&gt;&gt;&gt;</span>
</code></pre></div>
<p><a href="#creating-singletons">Later</a>, you will see an example defining a proper class decorator, namely <code>@singleton</code>, which ensures that there is only one instance of a class.</p>
<div class="w-100 text-center js-needs-scaling" style="transform-origin: 0 0;"><div id="waldo-tag-5261"></div> <a class="small text-muted js-disclosure" href="/account/join/" rel="nofollow" style="display: none;"> <i aria-hidden="true" class="fa fa-info-circle"> </i> Remove ads</a></div></section><section class="section3" id="nesting-decorators"><h3>Nesting Decorators<a class="headerlink" href="#nesting-decorators" title="Permanent link"></a></h3>
<p>You can <strong>apply several decorators</strong> to a function by stacking them on top of each other:</p>
<div class="highlight python"><pre><span></span><code><span class="kn">from</span> <span class="nn">decorators</span> <span class="kn">import</span> <span class="n">debug</span><span class="p">,</span> <span class="n">do_twice</span>
<span class="nd">@debug</span>
<span class="nd">@do_twice</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="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Hello </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
</code></pre></div>
<p>Think about this as the decorators being executed in the order they are listed. In other words, <code>@debug</code> calls <code>@do_twice</code>, which calls <code>greet()</code>, or <code>debug(do_twice(greet()))</code>:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">greet</span><span class="p">(</span><span class="s2">&quot;Eva&quot;</span><span class="p">)</span>
<span class="go">Calling greet(&#39;Eva&#39;)</span>
<span class="go">Hello Eva</span>
<span class="go">Hello Eva</span>
<span class="go">&#39;greet&#39; returned None</span>
</code></pre></div>
<p>Observe the difference if we change the order of <code>@debug</code> and <code>@do_twice</code>:</p>
<div class="highlight python"><pre><span></span><code><span class="kn">from</span> <span class="nn">decorators</span> <span class="kn">import</span> <span class="n">debug</span><span class="p">,</span> <span class="n">do_twice</span>
<span class="hll"><span class="nd">@do_twice</span>
</span><span class="hll"><span class="nd">@debug</span>
</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="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Hello </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
</code></pre></div>
<p>In this case, <code>@do_twice</code> will be applied to <code>@debug</code> as well:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">greet</span><span class="p">(</span><span class="s2">&quot;Eva&quot;</span><span class="p">)</span>
<span class="go">Calling greet(&#39;Eva&#39;)</span>
<span class="go">Hello Eva</span>
<span class="go">&#39;greet&#39; returned None</span>
<span class="go">Calling greet(&#39;Eva&#39;)</span>
<span class="go">Hello Eva</span>
<span class="go">&#39;greet&#39; returned None</span>
</code></pre></div>
</section><section class="section3" id="decorators-with-arguments"><h3>Decorators With Arguments<a class="headerlink" href="#decorators-with-arguments" title="Permanent link"></a></h3>
<p>Sometimes, it&rsquo;s useful to <strong>pass arguments to your decorators</strong>. For instance, <code>@do_twice</code> could be extended to a <code>@repeat(num_times)</code> decorator. The number of times to execute the decorated function could then be given as an argument.</p>
<p>This would allow you to do something like this:</p>
<div class="highlight python"><pre><span></span><code><span class="nd">@repeat</span><span class="p">(</span><span class="n">num_times</span><span class="o">=</span><span class="mi">4</span><span class="p">)</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="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Hello </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
</code></pre></div>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">greet</span><span class="p">(</span><span class="s2">&quot;World&quot;</span><span class="p">)</span>
<span class="go">Hello World</span>
<span class="go">Hello World</span>
<span class="go">Hello World</span>
<span class="go">Hello World</span>
</code></pre></div>
<p>Think about how you could achieve this.</p>
<p>So far, the name written after the <code>@</code> has referred to a function object that can be called with another function. To be consistent, you then need <code>repeat(num_times=4)</code> to return a function object that can act as a decorator. Luckily, you <a href="#returning-functions-from-functions">already know how to return functions</a>! In general, you want something like the following:</p>
<div class="highlight python"><pre><span></span><code><span class="k">def</span> <span class="nf">repeat</span><span class="p">(</span><span class="n">num_times</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">decorator_repeat</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
<span class="o">...</span> <span class="c1"># Create and return a wrapper function</span>
<span class="k">return</span> <span class="n">decorator_repeat</span>
</code></pre></div>
<p>Typically, the decorator creates and returns an inner wrapper function, so writing the example out in full will give you an inner function within an inner function. While this might sound like the programming equivalent of the <a href="https://en.wikipedia.org/wiki/Inception">Inception movie</a>, we&rsquo;ll untangle it all in a moment:</p>
<div class="highlight python"><pre><span></span><code><span class="k">def</span> <span class="nf">repeat</span><span class="p">(</span><span class="n">num_times</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">decorator_repeat</span><span class="p">(</span><span class="n">func</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_repeat</span><span class="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="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">num_times</span><span class="p">):</span>
<span class="n">value</span> <span class="o">=</span> <span class="n">func</span><span class="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="k">return</span> <span class="n">value</span>
<span class="k">return</span> <span class="n">wrapper_repeat</span>
<span class="k">return</span> <span class="n">decorator_repeat</span>
</code></pre></div>
<p>It looks a little messy, but we have only put the same decorator pattern you have seen many times by now inside one additional <code>def</code> that handles the arguments to the decorator. Let&rsquo;s start with the innermost function:</p>
<div class="highlight python"><pre><span></span><code><span class="k">def</span> <span class="nf">wrapper_repeat</span><span class="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="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">num_times</span><span class="p">):</span>
<span class="n">value</span> <span class="o">=</span> <span class="n">func</span><span class="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="k">return</span> <span class="n">value</span>
</code></pre></div>
<p>This <code>wrapper_repeat()</code> function takes arbitrary arguments and returns the value of the decorated function, <code>func()</code>. This wrapper function also contains the loop that calls the decorated function <code>num_times</code> times. This is no different from the earlier wrapper functions you have seen, except that it is using the <code>num_times</code> parameter that must be supplied from the outside.</p>
<p>One step out, you&rsquo;ll find the decorator function:</p>
<div class="highlight python"><pre><span></span><code><span class="k">def</span> <span class="nf">decorator_repeat</span><span class="p">(</span><span class="n">func</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_repeat</span><span class="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="o">...</span>
<span class="k">return</span> <span class="n">wrapper_repeat</span>
</code></pre></div>
<p>Again, <code>decorator_repeat()</code> looks exactly like the decorator functions you have written earlier, except that it&rsquo;s named differently. That&rsquo;s because we reserve the base name&mdash;<code>repeat()</code>&mdash;for the outermost function, which is the one the user will call.</p>
<p>As you have already seen, the outermost function returns a reference to the decorator function:</p>
<div class="highlight python"><pre><span></span><code><span class="k">def</span> <span class="nf">repeat</span><span class="p">(</span><span class="n">num_times</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">decorator_repeat</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
<span class="o">...</span>
<span class="k">return</span> <span class="n">decorator_repeat</span>
</code></pre></div>
<p>There are a few subtle things happening in the <code>repeat()</code> function:</p>
<ul>
<li>Defining <code>decorator_repeat()</code> as an inner function means that <code>repeat()</code> will refer to a function object&mdash;<code>decorator_repeat</code>. Earlier, we used <code>repeat</code> without parentheses to refer to the function object. The added parentheses are necessary when defining decorators that take arguments.</li>
<li>The <code>num_times</code> argument is seemingly not used in <code>repeat()</code> itself. But by passing <code>num_times</code> a <a href="https://realpython.com/inner-functions-what-are-they-good-for/">closure</a> is created where the value of <code>num_times</code> is stored until it will be used later by <code>wrapper_repeat()</code>.</li>
</ul>
<p>With everything set up, let&rsquo;s see if the results are as expected:</p>
<div class="highlight python"><pre><span></span><code><span class="nd">@repeat</span><span class="p">(</span><span class="n">num_times</span><span class="o">=</span><span class="mi">4</span><span class="p">)</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="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Hello </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
</code></pre></div>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">greet</span><span class="p">(</span><span class="s2">&quot;World&quot;</span><span class="p">)</span>
<span class="go">Hello World</span>
<span class="go">Hello World</span>
<span class="go">Hello World</span>
<span class="go">Hello World</span>
</code></pre></div>
<p>Just the result we were aiming for.</p>
<div class="w-100 text-center js-needs-scaling" style="transform-origin: 0 0;"><div id="waldo-tag-5263"></div> <a class="small text-muted js-disclosure" href="/account/join/" rel="nofollow" style="display: none;"> <i aria-hidden="true" class="fa fa-info-circle"> </i> Remove ads</a></div></section><section class="section3" id="both-please-but-never-mind-the-bread"><h3>Both Please, But Never Mind the Bread<a class="headerlink" href="#both-please-but-never-mind-the-bread" title="Permanent link"></a></h3>
<p>With a little bit of care, you can also define <strong>decorators that can be used both with and without arguments</strong>. Most likely, you don&rsquo;t need this, but it is nice to have the flexibility.</p>
<p>As you saw in the previous section, when a decorator uses arguments, you need to add an extra outer function. The challenge is for your code to figure out if the decorator has been called with or without arguments.</p>
<p>Since the function to decorate is only passed in directly if the decorator is called without arguments, the function must be an optional argument. This means that the decorator arguments must all be specified by keyword. You can enforce this with the special <code>*</code> syntax, which means that <a href="https://www.python.org/dev/peps/pep-3102/">all following parameters are keyword-only</a>:</p>
<div class="highlight python"><pre><span></span><code><span class="k">def</span> <span class="nf">name</span><span class="p">(</span><span class="n">_func</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">kw1</span><span class="o">=</span><span class="n">val1</span><span class="p">,</span> <span class="n">kw2</span><span class="o">=</span><span class="n">val2</span><span class="p">,</span> <span class="o">...</span><span class="p">):</span> <span class="c1"># 1</span>
<span class="k">def</span> <span class="nf">decorator_name</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
<span class="o">...</span> <span class="c1"># Create and return a wrapper function.</span>
<span class="k">if</span> <span class="n">_func</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">return</span> <span class="n">decorator_name</span> <span class="c1"># 2</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="n">decorator_name</span><span class="p">(</span><span class="n">_func</span><span class="p">)</span> <span class="c1"># 3</span>
</code></pre></div>
<p>Here, the <code>_func</code> argument acts as a marker, noting whether the decorator has been called with arguments or not:</p>
<ol>
<li>If <code>name</code> has been called without arguments, the decorated function will be passed in as <code>_func</code>. If it has been called with arguments, then <code>_func</code> will be <code>None</code>, and some of the keyword arguments may have been changed from their default values. The <code>*</code> in the argument list means that the remaining arguments can&rsquo;t be called as positional arguments.</li>
<li>In this case, the decorator was called with arguments. Return a decorator function that can read and return a function.</li>
<li>In this case, the decorator was called without arguments. Apply the decorator to the function immediately.</li>
</ol>
<p>Using this boilerplate on the <code>@repeat</code> decorator in the previous section, you can write the following:</p>
<div class="highlight python"><pre><span></span><code><span class="hll"><span class="k">def</span> <span class="nf">repeat</span><span class="p">(</span><span class="n">_func</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">num_times</span><span class="o">=</span><span class="mi">2</span><span class="p">):</span>
</span> <span class="k">def</span> <span class="nf">decorator_repeat</span><span class="p">(</span><span class="n">func</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_repeat</span><span class="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="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">num_times</span><span class="p">):</span>
<span class="n">value</span> <span class="o">=</span> <span class="n">func</span><span class="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="k">return</span> <span class="n">value</span>
<span class="k">return</span> <span class="n">wrapper_repeat</span>
<span class="hll"> <span class="k">if</span> <span class="n">_func</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
</span><span class="hll"> <span class="k">return</span> <span class="n">decorator_repeat</span>
</span><span class="hll"> <span class="k">else</span><span class="p">:</span>
</span><span class="hll"> <span class="k">return</span> <span class="n">decorator_repeat</span><span class="p">(</span><span class="n">_func</span><span class="p">)</span>
</span></code></pre></div>
<p>Compare this with the original <code>@repeat</code>. The only changes are the added <code>_func</code> parameter and the <code>if</code>-<code>else</code> at the end.</p>
<p><a href="https://github.com/dabeaz/python-cookbook/blob/master/src/9/defining_a_decorator_that_takes_an_optional_argument/example.py">Recipe 9.6</a> of the excellent <a href="https://realpython.com/asins/1449340377/">Python Cookbook</a> shows an alternative solution using <a href="https://docs.python.org/library/functools.html#functools.partial"><code>functools.partial()</code></a>.</p>
<p>These examples show that <code>@repeat</code> can now be used with or without arguments:</p>
<div class="highlight python"><pre><span></span><code><span class="nd">@repeat</span>
<span class="k">def</span> <span class="nf">say_whee</span><span class="p">():</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Whee!&quot;</span><span class="p">)</span>
<span class="nd">@repeat</span><span class="p">(</span><span class="n">num_times</span><span class="o">=</span><span class="mi">3</span><span class="p">)</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="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Hello </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
</code></pre></div>
<p>Recall that the default value of <code>num_times</code> is 2:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">say_whee</span><span class="p">()</span>
<span class="go">Whee!</span>
<span class="go">Whee!</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">greet</span><span class="p">(</span><span class="s2">&quot;Penny&quot;</span><span class="p">)</span>
<span class="go">Hello Penny</span>
<span class="go">Hello Penny</span>
<span class="go">Hello Penny</span>
</code></pre></div>
</section><section class="section3" id="stateful-decorators"><h3>Stateful Decorators<a class="headerlink" href="#stateful-decorators" title="Permanent link"></a></h3>
<p>Sometimes, it&rsquo;s useful to have <strong>a decorator that can keep track of state</strong>. As a simple example, we will create a decorator that counts the number of times a function is called.</p>
<div class="alert alert-primary" role="alert">
<p><strong>Note:</strong> In <a href="#functions">the beginning of this guide</a>, we talked about pure functions returning a value based on given arguments. Stateful decorators are quite the opposite, where the return value will depend on the current state, as well as the given arguments.</p>
</div>
<p>In the <a href="#classes-as-decorators">next section</a>, you will see how to use classes to keep state. But in simple cases, you can also get away with using <a href="https://www.python.org/dev/peps/pep-0232/">function attributes</a>:</p>
<div class="highlight python"><pre><span></span><code><span class="kn">import</span> <span class="nn">functools</span>
<span class="k">def</span> <span class="nf">count_calls</span><span class="p">(</span><span class="n">func</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_count_calls</span><span class="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">wrapper_count_calls</span><span class="o">.</span><span class="n">num_calls</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Call </span><span class="si">{</span><span class="n">wrapper_count_calls</span><span class="o">.</span><span class="n">num_calls</span><span class="si">}</span><span class="s2"> of </span><span class="si">{</span><span class="n">func</span><span class="o">.</span><span class="vm">__name__</span><span class="si">!r}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="n">func</span><span class="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">wrapper_count_calls</span><span class="o">.</span><span class="n">num_calls</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">return</span> <span class="n">wrapper_count_calls</span>
<span class="nd">@count_calls</span>
<span class="k">def</span> <span class="nf">say_whee</span><span class="p">():</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Whee!&quot;</span><span class="p">)</span>
</code></pre></div>
<p>The state&mdash;the number of calls to the function&mdash;is stored in the function attribute <code>.num_calls</code> on the wrapper function. Here is the effect of using it:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">say_whee</span><span class="p">()</span>
<span class="go">Call 1 of &#39;say_whee&#39;</span>
<span class="go">Whee!</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">say_whee</span><span class="p">()</span>
<span class="go">Call 2 of &#39;say_whee&#39;</span>
<span class="go">Whee!</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">say_whee</span><span class="o">.</span><span class="n">num_calls</span>
<span class="go">2</span>
</code></pre></div>
<div class="w-100 text-center js-needs-scaling" style="transform-origin: 0 0;"><div id="waldo-tag-5265"></div> <a class="small text-muted js-disclosure" href="/account/join/" rel="nofollow" style="display: none;"> <i aria-hidden="true" class="fa fa-info-circle"> </i> Remove ads</a></div></section><section class="section3" id="classes-as-decorators"><h3>Classes as Decorators<a class="headerlink" href="#classes-as-decorators" title="Permanent link"></a></h3>
<p>The typical way to maintain state is by <a href="https://realpython.com/python3-object-oriented-programming/">using classes</a>. In this section, you&rsquo;ll see how to rewrite the <code>@count_calls</code> example from the previous section <strong>using a class as a decorator</strong>.</p>
<p>Recall that the decorator syntax <code>@my_decorator</code> is just an easier way of saying <code>func = my_decorator(func)</code>. Therefore, if <code>my_decorator</code> is a class, it needs to take <code>func</code> as an argument in its <code>.__init__()</code> method. Furthermore, the class instance needs to be <a href="https://docs.python.org/reference/datamodel.html#emulating-callable-objects">callable</a> so that it can stand in for the decorated function.</p>
<p>For a class instance to be callable, you implement the special <code>.__call__()</code> method:</p>
<div class="highlight python"><pre><span></span><code><span class="k">class</span> <span class="nc">Counter</span><span class="p">:</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">start</span><span class="o">=</span><span class="mi">0</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">count</span> <span class="o">=</span> <span class="n">start</span>
<span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">count</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Current count is </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">count</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
</code></pre></div>
<p>The <code>.__call__()</code> method is executed each time you try to call an instance of the class:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">counter</span> <span class="o">=</span> <span class="n">Counter</span><span class="p">()</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">counter</span><span class="p">()</span>
<span class="go">Current count is 1</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">counter</span><span class="p">()</span>
<span class="go">Current count is 2</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">counter</span><span class="o">.</span><span class="n">count</span>
<span class="go">2</span>
</code></pre></div>
<p>Therefore, a typical implementation of a decorator class needs to implement <code>.__init__()</code> and <code>.__call__()</code>:</p>
<div class="highlight python"><pre><span></span><code><span class="kn">import</span> <span class="nn">functools</span>
<span class="k">class</span> <span class="nc">CountCalls</span><span class="p">:</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">func</span><span class="p">):</span>
<span class="n">functools</span><span class="o">.</span><span class="n">update_wrapper</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">func</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">func</span> <span class="o">=</span> <span class="n">func</span>
<span class="bp">self</span><span class="o">.</span><span class="n">num_calls</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="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="bp">self</span><span class="o">.</span><span class="n">num_calls</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Call </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">num_calls</span><span class="si">}</span><span class="s2"> of </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">func</span><span class="o">.</span><span class="vm">__name__</span><span class="si">!r}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">func</span><span class="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="nd">@CountCalls</span>
<span class="k">def</span> <span class="nf">say_whee</span><span class="p">():</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Whee!&quot;</span><span class="p">)</span>
</code></pre></div>
<p>The <code>.__init__()</code> method must store a reference to the function and can do any other necessary initialization. The <code>.__call__()</code> method will be called instead of the decorated function. It does essentially the same thing as the <code>wrapper()</code> function in our earlier examples. Note that you need to use the <a href="https://docs.python.org/library/functools.html#functools.update_wrapper"><code>functools.update_wrapper()</code></a> function instead of <code>@functools.wraps</code>.</p>
<p>This <code>@CountCalls</code> decorator works the same as the one in the previous section:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">say_whee</span><span class="p">()</span>
<span class="go">Call 1 of &#39;say_whee&#39;</span>
<span class="go">Whee!</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">say_whee</span><span class="p">()</span>
<span class="go">Call 2 of &#39;say_whee&#39;</span>
<span class="go">Whee!</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">say_whee</span><span class="o">.</span><span class="n">num_calls</span>
<span class="go">2</span>
</code></pre></div>
</section></section><section class="section2" id="more-real-world-examples"><h2>More Real World Examples<a class="headerlink" href="#more-real-world-examples" title="Permanent link"></a></h2>
<p>We&rsquo;ve come a far way now, having figured out how to create all kinds of decorators. Let&rsquo;s wrap it up, putting our newfound knowledge into creating a few more examples that might actually be useful in the real world.</p>
<section class="section3" id="slowing-down-code-revisited"><h3>Slowing Down Code, Revisited<a class="headerlink" href="#slowing-down-code-revisited" title="Permanent link"></a></h3>
<p>As noted earlier, our <a href="#slowing-down-code">previous implementation of <code>@slow_down</code></a> always sleeps for one second. Now you know how to add parameters to decorators, so let&rsquo;s rewrite <code>@slow_down</code> using an optional <code>rate</code> argument that controls how long it sleeps:</p>
<div class="highlight python"><pre><span></span><code><span class="kn">import</span> <span class="nn">functools</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="k">def</span> <span class="nf">slow_down</span><span class="p">(</span><span class="n">_func</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">rate</span><span class="o">=</span><span class="mi">1</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Sleep given amount of seconds before calling the function&quot;&quot;&quot;</span>
<span class="k">def</span> <span class="nf">decorator_slow_down</span><span class="p">(</span><span class="n">func</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_slow_down</span><span class="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">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="n">rate</span><span class="p">)</span>
<span class="k">return</span> <span class="n">func</span><span class="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="k">return</span> <span class="n">wrapper_slow_down</span>
<span class="k">if</span> <span class="n">_func</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">return</span> <span class="n">decorator_slow_down</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="n">decorator_slow_down</span><span class="p">(</span><span class="n">_func</span><span class="p">)</span>
</code></pre></div>
<p>We&rsquo;re using the boilerplate introduced in the <a href="#both-please-but-never-mind-the-bread">Both Please, But Never Mind the Bread</a> section to make <code>@slow_down</code> callable both with and without arguments. The same recursive <code>countdown()</code> function <a href="#slowing-down-code">as earlier</a> now sleeps two seconds between each count:</p>
<div class="highlight python"><pre><span></span><code><span class="hll"><span class="nd">@slow_down</span><span class="p">(</span><span class="n">rate</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
</span><span class="k">def</span> <span class="nf">countdown</span><span class="p">(</span><span class="n">from_number</span><span class="p">):</span>
<span class="k">if</span> <span class="n">from_number</span> <span class="o">&lt;</span> <span class="mi">1</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Liftoff!&quot;</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="n">from_number</span><span class="p">)</span>
<span class="n">countdown</span><span class="p">(</span><span class="n">from_number</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
</code></pre></div>
<p>As before, you must run the example yourself to see the effect of the decorator:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">countdown</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
<span class="go">3</span>
<span class="go">2</span>
<span class="go">1</span>
<span class="go">Liftoff!</span>
</code></pre></div>
</section><section class="section3" id="creating-singletons"><h3>Creating Singletons<a class="headerlink" href="#creating-singletons" title="Permanent link"></a></h3>
<p>A singleton is a class with only one instance. There are several singletons in Python that you use frequently, including <code>None</code>, <code>True</code>, and <code>False</code>. It is the fact that <code>None</code> is a singleton that allows you to compare for <code>None</code> using the <a href="https://realpython.com/python-is-identity-vs-equality/"><code>is</code> keyword</a>, like you saw in the <a href="#both-please-but-never-mind-the-bread">Both Please</a> section:</p>
<div class="highlight python"><pre><span></span><code><span class="k">if</span> <span class="n">_func</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">return</span> <span class="n">decorator_name</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="n">decorator_name</span><span class="p">(</span><span class="n">_func</span><span class="p">)</span>
</code></pre></div>
<p>Using <code>is</code> returns <code>True</code> only for objects that are the exact same instance. The following <code>@singleton</code> decorator turns a class into a singleton by storing the first instance of the class as an attribute. Later attempts at creating an instance simply return the stored instance:</p>
<div class="highlight python"><pre><span></span><code><span class="kn">import</span> <span class="nn">functools</span>
<span class="k">def</span> <span class="nf">singleton</span><span class="p">(</span><span class="bp">cls</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Make a class a Singleton class (only one instance)&quot;&quot;&quot;</span>
<span class="nd">@functools</span><span class="o">.</span><span class="n">wraps</span><span class="p">(</span><span class="bp">cls</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">wrapper_singleton</span><span class="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="k">if</span> <span class="ow">not</span> <span class="n">wrapper_singleton</span><span class="o">.</span><span class="n">instance</span><span class="p">:</span>
<span class="n">wrapper_singleton</span><span class="o">.</span><span class="n">instance</span> <span class="o">=</span> <span class="bp">cls</span><span class="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="k">return</span> <span class="n">wrapper_singleton</span><span class="o">.</span><span class="n">instance</span>
<span class="n">wrapper_singleton</span><span class="o">.</span><span class="n">instance</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">return</span> <span class="n">wrapper_singleton</span>
<span class="nd">@singleton</span>
<span class="k">class</span> <span class="nc">TheOne</span><span class="p">:</span>
<span class="k">pass</span>
</code></pre></div>
<p>As you see, this class decorator follows the same template as our function decorators. The only difference is that we are using <code>cls</code> instead of <code>func</code> as the parameter name to indicate that it is meant to be a class decorator.</p>
<p>Let&rsquo;s see if it works:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">first_one</span> <span class="o">=</span> <span class="n">TheOne</span><span class="p">()</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">another_one</span> <span class="o">=</span> <span class="n">TheOne</span><span class="p">()</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">id</span><span class="p">(</span><span class="n">first_one</span><span class="p">)</span>
<span class="go">140094218762280</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">id</span><span class="p">(</span><span class="n">another_one</span><span class="p">)</span>
<span class="go">140094218762280</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">first_one</span> <span class="ow">is</span> <span class="n">another_one</span>
<span class="go">True</span>
</code></pre></div>
<p>It seems clear that <code>first_one</code> is indeed the exact same instance as <code>another_one</code>.</p>
<div class="alert alert-primary" role="alert">
<p><strong>Note:</strong> Singleton classes are not really used as often in Python as in other languages. The effect of a singleton is usually better implemented as a global variable in a module.</p>
</div>
</section><section class="section3" id="caching-return-values"><h3>Caching Return Values<a class="headerlink" href="#caching-return-values" title="Permanent link"></a></h3>
<p>Decorators can provide a nice mechanism for <a href="https://en.wikipedia.org/wiki/Cache_%28computing%29">caching</a> and <a href="https://en.wikipedia.org/wiki/Memoization">memoization</a>. As an example, let&rsquo;s look at a <a href="https://realpython.com/python-thinking-recursively/">recursive</a> definition of the <a href="https://en.wikipedia.org/wiki/Fibonacci_number">Fibonacci sequence</a>:</p>
<div class="highlight python"><pre><span></span><code><span class="kn">from</span> <span class="nn">decorators</span> <span class="kn">import</span> <span class="n">count_calls</span>
<span class="nd">@count_calls</span>
<span class="k">def</span> <span class="nf">fibonacci</span><span class="p">(</span><span class="n">num</span><span class="p">):</span>
<span class="k">if</span> <span class="n">num</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="p">:</span>
<span class="k">return</span> <span class="n">num</span>
<span class="k">return</span> <span class="n">fibonacci</span><span class="p">(</span><span class="n">num</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="n">fibonacci</span><span class="p">(</span><span class="n">num</span> <span class="o">-</span> <span class="mi">2</span><span class="p">)</span>
</code></pre></div>
<p>While the implementation is simple, its runtime performance is terrible:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">fibonacci</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
<span class="go">&lt;Lots of output from count_calls&gt;</span>
<span class="go">55</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">fibonacci</span><span class="o">.</span><span class="n">num_calls</span>
<span class="go">177</span>
</code></pre></div>
<p>To calculate the tenth Fibonacci number, you should really only need to calculate the preceding Fibonacci numbers, but this implementation somehow needs a whopping 177 calculations. It gets worse quickly: 21891 calculations are needed for <code>fibonacci(20)</code> and almost 2.7 million calculations for the 30th number. This is because the code keeps recalculating Fibonacci numbers that are already known.</p>
<p>The usual solution is to implement Fibonacci numbers using a <code>for</code> loop and a lookup table. However, simple caching of the calculations will also do the trick:</p>
<div class="highlight python"><pre><span></span><code><span class="kn">import</span> <span class="nn">functools</span>
<span class="kn">from</span> <span class="nn">decorators</span> <span class="kn">import</span> <span class="n">count_calls</span>
<span class="k">def</span> <span class="nf">cache</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Keep a cache of previous function calls&quot;&quot;&quot;</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_cache</span><span class="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">cache_key</span> <span class="o">=</span> <span class="n">args</span> <span class="o">+</span> <span class="nb">tuple</span><span class="p">(</span><span class="n">kwargs</span><span class="o">.</span><span class="n">items</span><span class="p">())</span>
<span class="k">if</span> <span class="n">cache_key</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">wrapper_cache</span><span class="o">.</span><span class="n">cache</span><span class="p">:</span>
<span class="n">wrapper_cache</span><span class="o">.</span><span class="n">cache</span><span class="p">[</span><span class="n">cache_key</span><span class="p">]</span> <span class="o">=</span> <span class="n">func</span><span class="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="k">return</span> <span class="n">wrapper_cache</span><span class="o">.</span><span class="n">cache</span><span class="p">[</span><span class="n">cache_key</span><span class="p">]</span>
<span class="n">wrapper_cache</span><span class="o">.</span><span class="n">cache</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">()</span>
<span class="k">return</span> <span class="n">wrapper_cache</span>
<span class="hll"><span class="nd">@cache</span>
</span><span class="nd">@count_calls</span>
<span class="k">def</span> <span class="nf">fibonacci</span><span class="p">(</span><span class="n">num</span><span class="p">):</span>
<span class="k">if</span> <span class="n">num</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="p">:</span>
<span class="k">return</span> <span class="n">num</span>
<span class="k">return</span> <span class="n">fibonacci</span><span class="p">(</span><span class="n">num</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="n">fibonacci</span><span class="p">(</span><span class="n">num</span> <span class="o">-</span> <span class="mi">2</span><span class="p">)</span>
</code></pre></div>
<p>The cache works as a lookup table, so now <code>fibonacci()</code> only does the necessary calculations once:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">fibonacci</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
<span class="go">Call 1 of &#39;fibonacci&#39;</span>
<span class="gp">...</span>
<span class="go">Call 11 of &#39;fibonacci&#39;</span>
<span class="go">55</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">fibonacci</span><span class="p">(</span><span class="mi">8</span><span class="p">)</span>
<span class="go">21</span>
</code></pre></div>
<p>Note that in the final call to <code>fibonacci(8)</code>, no new calculations were needed, since the eighth Fibonacci number had already been calculated for <code>fibonacci(10)</code>.</p>
<p>In the standard library, a <a href="https://realpython.com/lru-cache-python/">Least Recently Used (LRU) cache</a> is available as <a href="https://docs.python.org/library/functools.html#functools.lru_cache"><code>@functools.lru_cache</code></a>.</p>
<p>This decorator has more features than the one you saw above. You should use <code>@functools.lru_cache</code> instead of writing your own cache decorator:</p>
<div class="highlight python"><pre><span></span><code><span class="kn">import</span> <span class="nn">functools</span>
<span class="hll"><span class="nd">@functools</span><span class="o">.</span><span class="n">lru_cache</span><span class="p">(</span><span class="n">maxsize</span><span class="o">=</span><span class="mi">4</span><span class="p">)</span>
</span><span class="k">def</span> <span class="nf">fibonacci</span><span class="p">(</span><span class="n">num</span><span class="p">):</span>
<span class="hll"> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Calculating fibonacci(</span><span class="si">{</span><span class="n">num</span><span class="si">}</span><span class="s2">)&quot;</span><span class="p">)</span>
</span> <span class="k">if</span> <span class="n">num</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="p">:</span>
<span class="k">return</span> <span class="n">num</span>
<span class="k">return</span> <span class="n">fibonacci</span><span class="p">(</span><span class="n">num</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="n">fibonacci</span><span class="p">(</span><span class="n">num</span> <span class="o">-</span> <span class="mi">2</span><span class="p">)</span>
</code></pre></div>
<p>The <code>maxsize</code> parameter specifies how many recent calls are cached. The default value is 128, but you can specify <code>maxsize=None</code> to cache all function calls. However, be aware that this can cause memory problems if you are caching many large objects.</p>
<p>You can use the <code>.cache_info()</code> method to see how the cache performs, and you can tune it if needed. In our example, we used an artificially small <code>maxsize</code> to see the effect of elements being removed from the cache:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">fibonacci</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
<span class="go">Calculating fibonacci(10)</span>
<span class="go">Calculating fibonacci(9)</span>
<span class="go">Calculating fibonacci(8)</span>
<span class="go">Calculating fibonacci(7)</span>
<span class="go">Calculating fibonacci(6)</span>
<span class="go">Calculating fibonacci(5)</span>
<span class="go">Calculating fibonacci(4)</span>
<span class="go">Calculating fibonacci(3)</span>
<span class="go">Calculating fibonacci(2)</span>
<span class="go">Calculating fibonacci(1)</span>
<span class="go">Calculating fibonacci(0)</span>
<span class="go">55</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">fibonacci</span><span class="p">(</span><span class="mi">8</span><span class="p">)</span>
<span class="go">21</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">fibonacci</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
<span class="go">Calculating fibonacci(5)</span>
<span class="go">Calculating fibonacci(4)</span>
<span class="go">Calculating fibonacci(3)</span>
<span class="go">Calculating fibonacci(2)</span>
<span class="go">Calculating fibonacci(1)</span>
<span class="go">Calculating fibonacci(0)</span>
<span class="go">5</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">fibonacci</span><span class="p">(</span><span class="mi">8</span><span class="p">)</span>
<span class="go">Calculating fibonacci(8)</span>
<span class="go">Calculating fibonacci(7)</span>
<span class="go">Calculating fibonacci(6)</span>
<span class="go">21</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">fibonacci</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
<span class="go">5</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">fibonacci</span><span class="o">.</span><span class="n">cache_info</span><span class="p">()</span>
<span class="go">CacheInfo(hits=17, misses=20, maxsize=4, currsize=4)</span>
</code></pre></div>
</section><section class="section3" id="adding-information-about-units"><h3>Adding Information About Units<a class="headerlink" href="#adding-information-about-units" title="Permanent link"></a></h3>
<p>The following example is somewhat similar to the <a href="#registering-plugins">Registering Plugins</a> example from earlier, in that it does not really change the behavior of the decorated function. Instead, it simply adds <code>unit</code> as a function attribute:</p>
<div class="highlight python"><pre><span></span><code><span class="k">def</span> <span class="nf">set_unit</span><span class="p">(</span><span class="n">unit</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Register a unit on a function&quot;&quot;&quot;</span>
<span class="k">def</span> <span class="nf">decorator_set_unit</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
<span class="n">func</span><span class="o">.</span><span class="n">unit</span> <span class="o">=</span> <span class="n">unit</span>
<span class="k">return</span> <span class="n">func</span>
<span class="k">return</span> <span class="n">decorator_set_unit</span>
</code></pre></div>
<p>The following example calculates the volume of a cylinder based on its radius and height in centimeters:</p>
<div class="highlight python"><pre><span></span><code><span class="kn">import</span> <span class="nn">math</span>
<span class="nd">@set_unit</span><span class="p">(</span><span class="s2">&quot;cm^3&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">volume</span><span class="p">(</span><span class="n">radius</span><span class="p">,</span> <span class="n">height</span><span class="p">):</span>
<span class="k">return</span> <span class="n">math</span><span class="o">.</span><span class="n">pi</span> <span class="o">*</span> <span class="n">radius</span><span class="o">**</span><span class="mi">2</span> <span class="o">*</span> <span class="n">height</span>
</code></pre></div>
<p>This <code>.unit</code> function attribute can later be accessed when needed:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">volume</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span>
<span class="go">141.3716694115407</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">volume</span><span class="o">.</span><span class="n">unit</span>
<span class="go">&#39;cm^3&#39;</span>
</code></pre></div>
<p>Note that you could have achieved something similar using <a href="https://www.python.org/dev/peps/pep-3107/">function annotations</a>:</p>
<div class="highlight python"><pre><span></span><code><span class="kn">import</span> <span class="nn">math</span>
<span class="hll"><span class="k">def</span> <span class="nf">volume</span><span class="p">(</span><span class="n">radius</span><span class="p">,</span> <span class="n">height</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="s2">&quot;cm^3&quot;</span><span class="p">:</span>
</span> <span class="k">return</span> <span class="n">math</span><span class="o">.</span><span class="n">pi</span> <span class="o">*</span> <span class="n">radius</span><span class="o">**</span><span class="mi">2</span> <span class="o">*</span> <span class="n">height</span>
</code></pre></div>
<p>However, since annotations are <a href="https://www.python.org/dev/peps/pep-0484/">used for type hints</a>, it would be hard to combine such units as annotations with <a href="https://realpython.com/python-type-checking/#static-type-checking">static type checking</a>.</p>
<p>Units become even more powerful and fun when connected with a library that can convert between units. One such library is <a href="http://pint.readthedocs.io/"><code>pint</code></a>. With <code>pint</code> installed (<a href="https://pypi.org/project/Pint/"><code>pip install Pint</code></a>), you can for instance convert the volume to cubic inches or gallons:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">pint</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">ureg</span> <span class="o">=</span> <span class="n">pint</span><span class="o">.</span><span class="n">UnitRegistry</span><span class="p">()</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">vol</span> <span class="o">=</span> <span class="n">volume</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span> <span class="o">*</span> <span class="n">ureg</span><span class="p">(</span><span class="n">volume</span><span class="o">.</span><span class="n">unit</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">vol</span>
<span class="go">&lt;Quantity(141.3716694115407, &#39;centimeter ** 3&#39;)&gt;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">vol</span><span class="o">.</span><span class="n">to</span><span class="p">(</span><span class="s2">&quot;cubic inches&quot;</span><span class="p">)</span>
<span class="go">&lt;Quantity(8.627028576414954, &#39;inch ** 3&#39;)&gt;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">vol</span><span class="o">.</span><span class="n">to</span><span class="p">(</span><span class="s2">&quot;gallons&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">m</span> <span class="c1"># Magnitude</span>
<span class="go">0.0373464440537444</span>
</code></pre></div>
<p>You could also modify the decorator to return a <code>pint</code> <a href="https://pint.readthedocs.io/en/latest/tutorial.html"><code>Quantity</code></a> directly. Such a <code>Quantity</code> is made by multiplying a value with the unit. In <code>pint</code>, units must be looked up in a <code>UnitRegistry</code>. The registry is stored as a function attribute to avoid cluttering the namespace:</p>
<div class="highlight python"><pre><span></span><code><span class="k">def</span> <span class="nf">use_unit</span><span class="p">(</span><span class="n">unit</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Have a function return a Quantity with given unit&quot;&quot;&quot;</span>
<span class="n">use_unit</span><span class="o">.</span><span class="n">ureg</span> <span class="o">=</span> <span class="n">pint</span><span class="o">.</span><span class="n">UnitRegistry</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">decorator_use_unit</span><span class="p">(</span><span class="n">func</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_use_unit</span><span class="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">value</span> <span class="o">=</span> <span class="n">func</span><span class="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="k">return</span> <span class="n">value</span> <span class="o">*</span> <span class="n">use_unit</span><span class="o">.</span><span class="n">ureg</span><span class="p">(</span><span class="n">unit</span><span class="p">)</span>
<span class="k">return</span> <span class="n">wrapper_use_unit</span>
<span class="k">return</span> <span class="n">decorator_use_unit</span>
<span class="nd">@use_unit</span><span class="p">(</span><span class="s2">&quot;meters per second&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">average_speed</span><span class="p">(</span><span class="n">distance</span><span class="p">,</span> <span class="n">duration</span><span class="p">):</span>
<span class="k">return</span> <span class="n">distance</span> <span class="o">/</span> <span class="n">duration</span>
</code></pre></div>
<p>With the <code>@use_unit</code> decorator, converting units is practically effortless:</p>
<div class="highlight python repl"><span class="repl-toggle" title="Toggle REPL prompts and output">&gt;&gt;&gt;</span><pre><span></span><code><span class="gp">&gt;&gt;&gt; </span><span class="n">bolt</span> <span class="o">=</span> <span class="n">average_speed</span><span class="p">(</span><span class="mi">100</span><span class="p">,</span> <span class="mf">9.58</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">bolt</span>
<span class="go">&lt;Quantity(10.438413361169102, &#39;meter / second&#39;)&gt;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">bolt</span><span class="o">.</span><span class="n">to</span><span class="p">(</span><span class="s2">&quot;km per hour&quot;</span><span class="p">)</span>
<span class="go">&lt;Quantity(37.578288100208766, &#39;kilometer / hour&#39;)&gt;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">bolt</span><span class="o">.</span><span class="n">to</span><span class="p">(</span><span class="s2">&quot;mph&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">m</span> <span class="c1"># Magnitude</span>
<span class="go">23.350065679064745</span>
</code></pre></div>
</section><section class="section3" id="validating-json"><h3>Validating JSON<a class="headerlink" href="#validating-json" title="Permanent link"></a></h3>
<p>Let&rsquo;s look at one last use case. Take a quick look at the following <a href="https://realpython.com/tutorials/flask/">Flask</a> route handler:</p>
<div class="highlight python"><pre><span></span><code><span class="nd">@app</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/grade&quot;</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;POST&quot;</span><span class="p">])</span>
<span class="k">def</span> <span class="nf">update_grade</span><span class="p">():</span>
<span class="n">json_data</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">get_json</span><span class="p">()</span>
<span class="k">if</span> <span class="s2">&quot;student_id&quot;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">json_data</span><span class="p">:</span>
<span class="n">abort</span><span class="p">(</span><span class="mi">400</span><span class="p">)</span>
<span class="c1"># Update database</span>
<span class="k">return</span> <span class="s2">&quot;success!&quot;</span>
</code></pre></div>
<p>Here we ensure that the key <code>student_id</code> is part of the request. Although this validation works, it really does not belong in the function itself. Plus, perhaps there are other routes that use the exact same validation. So, let&rsquo;s keep it <a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself">DRY</a> and abstract out any unnecessary logic with a decorator. The following <code>@validate_json</code> decorator will do the job:</p>
<div class="highlight python"><pre><span></span><code><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">abort</span>
<span class="kn">import</span> <span class="nn">functools</span>
<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="vm">__name__</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">validate_json</span><span class="p">(</span><span class="o">*</span><span class="n">expected_args</span><span class="p">):</span> <span class="c1"># 1</span>
<span class="k">def</span> <span class="nf">decorator_validate_json</span><span class="p">(</span><span class="n">func</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_validate_json</span><span class="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">json_object</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">get_json</span><span class="p">()</span>
<span class="k">for</span> <span class="n">expected_arg</span> <span class="ow">in</span> <span class="n">expected_args</span><span class="p">:</span> <span class="c1"># 2</span>
<span class="k">if</span> <span class="n">expected_arg</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">json_object</span><span class="p">:</span>
<span class="n">abort</span><span class="p">(</span><span class="mi">400</span><span class="p">)</span>
<span class="k">return</span> <span class="n">func</span><span class="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="k">return</span> <span class="n">wrapper_validate_json</span>
<span class="k">return</span> <span class="n">decorator_validate_json</span>
</code></pre></div>
<p>In the above code, the decorator takes a variable length list as an argument so that we can pass in as many string arguments as necessary, each representing a key used to validate the <a href="https://realpython.com/python-json/">JSON</a> data:</p>
<ol>
<li>The list of keys that must be present in the JSON is given as arguments to the decorator.</li>
<li>The wrapper function validates that each expected key is present in the JSON data.</li>
</ol>
<p>The route handler can then focus on its real job&mdash;updating grades&mdash;as it can safely assume that JSON data are valid:</p>
<div class="highlight python"><pre><span></span><code><span class="nd">@app</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/grade&quot;</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;POST&quot;</span><span class="p">])</span>
<span class="nd">@validate_json</span><span class="p">(</span><span class="s2">&quot;student_id&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">update_grade</span><span class="p">():</span>
<span class="n">json_data</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">get_json</span><span class="p">()</span>
<span class="c1"># Update database.</span>
<span class="k">return</span> <span class="s2">&quot;success!&quot;</span>
</code></pre></div>
</section></section><section class="section2" id="conclusion"><h2>Conclusion<a class="headerlink" href="#conclusion" title="Permanent link"></a></h2>
<p>This has been quite a journey! You started this tutorial by looking a little closer at functions, particularly how they can be defined inside other functions and passed around just like any other Python object. Then you learned about decorators and how to write them such that:</p>
<ul>
<li>They can be reused.</li>
<li>They can decorate functions with arguments and return values.</li>
<li>They can use <code>@functools.wraps</code> to look more like the decorated function.</li>
</ul>
<p>In the second part of the tutorial, you saw more advanced decorators and learned how to:</p>
<ul>
<li>Decorate classes</li>
<li>Nest decorators</li>
<li>Add arguments to decorators</li>
<li>Keep state within decorators</li>
<li>Use classes as decorators</li>
</ul>
<p>You saw that, to define a decorator, you typically define a function returning a wrapper function. The wrapper function uses <code>*args</code> and <code>**kwargs</code> to pass on arguments to the decorated function. If you want your decorator to also take arguments, you need to nest the wrapper function inside another function. In this case, you usually end up with three <code>return</code> statements.</p>
<p>You can find the <a href="https://github.com/realpython/materials/tree/master/primer-on-python-decorators">code from this tutorial online</a>.</p>
</section><section class="section2" id="further-reading"><h2>Further Reading<a class="headerlink" href="#further-reading" title="Permanent link"></a></h2>
<p>If you are still looking for more, our book <a href="https://realpython.com/products/python-tricks-book/">Python Tricks</a> has a section on decorators, as does the <a href="https://realpython.com/asins/1449340377/">Python Cookbook</a> by David Beazley and Brian K. Jones.</p>
<p>For a deep dive into the historical discussion on how decorators should be implemented in Python, see <a href="https://www.python.org/dev/peps/pep-0318/">PEP 318</a> as well as the <a href="https://wiki.python.org/moin/PythonDecorators">Python Decorator Wiki</a>. More examples of decorators can be found in the <a href="https://wiki.python.org/moin/PythonDecoratorLibrary">Python Decorator Library</a>. The <a href="https://github.com/micheles/decorator"><code>decorator</code> module</a> can simplify creating your own decorators, and its <a href="https://decorator.readthedocs.io">documentation</a> contains further decorator examples.</p>
<p>Also, we&rsquo;ve put together a short &amp; sweet Python decorators cheat sheet for you:</p>
<div class="alert alert-warning" role="alert">
<p><strong markdown="1">Decorators Cheat Sheet:</strong> <a href="https://realpython.com/bonus/decorators-cheatsheet/" class="alert-link" data-toggle="modal" data-target="#modal-decorators-cheatsheet" data-focus="false" markdown="1">Click here to get access to a free 3-page Python decorators cheat sheet</a> that summarizes the techniques explained in this tutorial.</p>
</div>
</section>
<div class="border rounded p-3 card mb-2">
<p class="mb-0"><span class="badge badge-pill badge-success"><i class="fa fa-play-circle" aria-hidden="true"></i> 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/python-decorators-101/"><strong>Python Decorators 101</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 &amp; 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 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="APHcibHwrBuqt3ULEi1fpqoAksKrzZcjzkcOoObHwIKXPQcmqbJWzjIZjTh8JogZ">
<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&amp;w=800&amp;h=800&amp;mode=crop&amp;sig=e9b761c6cf1359953014dba05554f5424eb116e1" srcset="https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/gahjelle.470149ee709e.jpg&amp;w=200&amp;h=200&amp;mode=crop&amp;sig=c6390201e73d3e09429d73da5bb29c17ab10403a 200w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/gahjelle.470149ee709e.jpg&amp;w=400&amp;h=400&amp;mode=crop&amp;sig=fcea459ee24a7b320573cadee324cf75509dc1d6 400w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/gahjelle.470149ee709e.jpg&amp;w=800&amp;h=800&amp;mode=crop&amp;sig=e9b761c6cf1359953014dba05554f5424eb116e1 800w" sizes="25vw" width="800" height="800" 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&amp;w=800&amp;h=800&amp;mode=crop&amp;sig=e9b761c6cf1359953014dba05554f5424eb116e1" srcset="https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/gahjelle.470149ee709e.jpg&amp;w=200&amp;h=200&amp;mode=crop&amp;sig=c6390201e73d3e09429d73da5bb29c17ab10403a 200w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/gahjelle.470149ee709e.jpg&amp;w=400&amp;h=400&amp;mode=crop&amp;sig=fcea459ee24a7b320573cadee324cf75509dc1d6 400w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/gahjelle.470149ee709e.jpg&amp;w=800&amp;h=800&amp;mode=crop&amp;sig=e9b761c6cf1359953014dba05554f5424eb116e1 800w" sizes="25vw" width="800" height="800" 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/asantos-avatar.888c78fffab3.jpg&amp;w=700&amp;h=700&amp;mode=crop&amp;sig=be17609cd7f6a4cd249ff61e186b3ad1aece949c" srcset="https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/asantos-avatar.888c78fffab3.jpg&amp;w=175&amp;h=175&amp;mode=crop&amp;sig=30253487c673fd67bb45ea6bef95b3c57e115e46 175w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/asantos-avatar.888c78fffab3.jpg&amp;w=350&amp;h=350&amp;mode=crop&amp;sig=a43bd9ea30a6f61a4fc2451ab9abfc6faec8a2c0 350w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/asantos-avatar.888c78fffab3.jpg&amp;w=700&amp;h=700&amp;mode=crop&amp;sig=be17609cd7f6a4cd249ff61e186b3ad1aece949c 700w" sizes="10vw" width="800" height="800" 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/bsolomon/"><img loading="lazy" src="https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/bsolomon.2a5a05833ac0.jpg&amp;w=938&amp;h=938&amp;mode=crop&amp;sig=a9beb357e2cfca40c0fd24da0d7543253f7fb10e" srcset="https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/bsolomon.2a5a05833ac0.jpg&amp;w=234&amp;h=234&amp;mode=crop&amp;sig=745b89b17845841a5c4d667e619518a6aa22d0a4 234w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/bsolomon.2a5a05833ac0.jpg&amp;w=469&amp;h=469&amp;mode=crop&amp;sig=af2598277ff415557b3331061f0d41462d29a48f 469w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/bsolomon.2a5a05833ac0.jpg&amp;w=938&amp;h=938&amp;mode=crop&amp;sig=a9beb357e2cfca40c0fd24da0d7543253f7fb10e 938w" sizes="10vw" width="800" height="800" class="rounded-circle img-fluid w-100" alt="Brad Solomon"></a>
</div>
<div class="col pl-0 d-none d-sm-block">
<a href="/team/bsolomon/" class="card-link small"><p>Brad</p></a>
</div>
<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&amp;w=1000&amp;h=1000&amp;mode=crop&amp;sig=304f5f568993310d5e87b2ca3c504260c018effa" srcset="https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/daniel-square.d58bf4388750.jpg&amp;w=250&amp;h=250&amp;mode=crop&amp;sig=981634b4528584f2e9c7ee663477173599e1781a 250w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/daniel-square.d58bf4388750.jpg&amp;w=500&amp;h=500&amp;mode=crop&amp;sig=841025b97d25f05c1b90802032e477020462fe01 500w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/daniel-square.d58bf4388750.jpg&amp;w=1000&amp;h=1000&amp;mode=crop&amp;sig=304f5f568993310d5e87b2ca3c504260c018effa 1000w" sizes="10vw" width="800" height="800" 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>
<div class="row align-items-center w-100 mx-auto">
<div class="col-4 col-sm-2 align-self-center">
<a href="/team/jjablonski/"><img loading="lazy" src="https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/jjablonksi-avatar.e37c4f83308e.jpg&amp;w=800&amp;h=800&amp;mode=crop&amp;sig=c363b704eeccb35f2247db13baff3d4383459858" srcset="https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/jjablonksi-avatar.e37c4f83308e.jpg&amp;w=200&amp;h=200&amp;mode=crop&amp;sig=706b16de3cb88a8f353f4a98d7c7bc7234229bd0 200w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/jjablonksi-avatar.e37c4f83308e.jpg&amp;w=400&amp;h=400&amp;mode=crop&amp;sig=6d7aa672ca3f1ac5f7cd62ed1641b60f98d04d8b 400w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/jjablonksi-avatar.e37c4f83308e.jpg&amp;w=800&amp;h=800&amp;mode=crop&amp;sig=c363b704eeccb35f2247db13baff3d4383459858 800w" sizes="10vw" width="800" height="800" class="rounded-circle img-fluid w-100" alt="Joanna Jablonski"></a>
</div>
<div class="col pl-0 d-none d-sm-block">
<a href="/team/jjablonski/" class="card-link small"><p>Joanna</p></a>
</div>
<div class="col-4 col-sm-2 align-self-center">
<a href="/team/mherman/"><img loading="lazy" src="https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/mike.fa94729a1e81.jpg&amp;w=160&amp;h=160&amp;mode=crop&amp;sig=edbc2adbb770d6a7dbb53b24a4cb3c0924927a9c" srcset="https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/mike.fa94729a1e81.jpg&amp;w=40&amp;h=40&amp;mode=crop&amp;sig=d205b98ae5f7d6680f3f2f3a1cd74a15eeee9f17 40w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/mike.fa94729a1e81.jpg&amp;w=80&amp;h=80&amp;mode=crop&amp;sig=8191692eba0f009f0beb44a7bd789533c0a5c213 80w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/mike.fa94729a1e81.jpg&amp;w=160&amp;h=160&amp;mode=crop&amp;sig=edbc2adbb770d6a7dbb53b24a4cb3c0924927a9c 160w" sizes="10vw" width="800" height="800" class="rounded-circle img-fluid w-100" alt="Michael Herman"></a>
</div>
<div class="col pl-0 d-none d-sm-block">
<a href="/team/mherman/" class="card-link small"><p>Michael</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</mark></u> With Unlimited Access to Real&nbsp;Python</p>
<p class="mb-1"><img class="w-75" src="/static/videos/lesson-locked.f5105cfd26db.svg"></p>
<p class="mx-auto w-75 mb-3 small"><strong>Join us and get access to hundreds of tutorials, hands-on video courses, and a community of expert&nbsp;Pythonistas:</strong></p>
<p class="mb-0"><a href="/account/join/?utm_source=rp_article_footer&utm_content=primer-on-python-decorators" class="btn btn-primary btn-sm px-4 mb-0">Level Up Your Python Skills »</a>
</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&nbsp;Python</p>
<p class="mb-2"><img class="w-50 mb-2" src="/static/videos/lesson-locked.f5105cfd26db.svg"></p>
<p class="mx-auto w-50 mb-3"><strong>Join us and get access to hundreds 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=primer-on-python-decorators" class="btn btn-primary btn-lg px-4">Level Up Your Python Skills »</a>
</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">
<span>
<a target="_blank" rel="nofollow" href="https://twitter.com/intent/tweet/?text=Check out this %23Python tutorial: Primer%20on%20Python%20Decorators by @realpython&url=https%3A//realpython.com/primer-on-python-decorators/" class="mr-1 badge badge-twitter text-light mb-1"><i class="mr-1 fa fa-twitter text-light"></i>Tweet</a>
<a target="_blank" rel="nofollow" href="https://facebook.com/sharer/sharer.php?u=https%3A//realpython.com/primer-on-python-decorators/" class="mr-1 badge badge-facebook text-light mb-1"><i class="mr-1 fa fa-facebook text-light"></i>Share</a>
<a target="_blank" rel="nofollow" href="mailto:?subject=Python article for you&body=Check out this Python tutorial:%0A%0APrimer%20on%20Python%20Decorators%0A%0Ahttps%3A//realpython.com/primer-on-python-decorators/" class="mr-1 badge badge-red text-light mb-1"><i class="mr-1 fa fa-envelope text-light"></i>Email</a>
</span>
</div>
<div class="card-body">
<div class="alert alert-dark">
<p class="mb-0"><strong>Real Python Comment Policy:</strong> The most useful comments are those written with the goal of learning from or helping out other readers—after reading the whole article and all the earlier comments. Complaints and insults generally wont make the cut here.</p>
</div>
<p>Whats 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="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/python-decorators-101/">Python Decorators 101</a></p>
</div>
</div>
<div class="modal fade" tabindex="-1" role="dialog" id="rpvc">
<div class="modal-dialog modal-lg modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header border-0 mt-3">
<div class="col-12 modal-title text-center">
<h2 class="my-0 mx-5">Master <u>Real-World Python Skills</u> With Unlimited Access to Real Python</h2>
<p class="text-center text-muted mt-2 mb-1">Already a member? <a href="/account/login/">Sign-In</a></p>
</div>
</div>
<div class="modal-body bg-light">
<div class="col-12 text-center">
<p class="mb-2 mt-3"><a href="/account/join/?utm_source=rp&utm_medium=web&utm_campaign=pwn&utm_content=v2"><img class="w-50 mb-2" src="/static/videos/lesson-locked.f5105cfd26db.svg"></a></p>
<p class="mx-auto w-66 mb-3"><strong>Join us and get access to hundreds of tutorials, hands-on video courses, and a community of expert Pythonistas:</strong></p>
<p><a href="/account/join/?utm_source=rp&utm_medium=web&utm_campaign=pwn&utm_content=v2" class="btn btn-primary btn-lg px-4"><i class="fa fa-unlock mr-2"></i>Unlock All Content »</a></a>
</div>
</div>
<div class="modal-footer border-0">
<button class="text-muted btn" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</div>
<aside class="col-md-7 col-lg-4">
<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 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="APHcibHwrBuqt3ULEi1fpqoAksKrzZcjzkcOoObHwIKXPQcmqbJWzjIZjTh8JogZ">
<input type="hidden" name="slug" value="static-python-tricks-sidebar">
<input type="email" class="form-control form-control-md" name="email" placeholder="Email&hellip;" 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">
<p class="h4"><a class="link-unstyled" href="/tutorials/all/">All Tutorial Topics</a></p>
<a href="/tutorials/advanced/" class="badge badge-light text-muted">advanced</a>
<a href="/tutorials/api/" class="badge badge-light text-muted">api</a>
<a href="/tutorials/basics/" class="badge badge-light text-muted">basics</a>
<a href="/tutorials/best-practices/" class="badge badge-light text-muted">best-practices</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/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/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/gui/" class="badge badge-light text-muted">gui</a>
<a href="/tutorials/intermediate/" class="badge badge-light text-muted">intermediate</a>
<a href="/tutorials/machine-learning/" class="badge badge-light text-muted">machine-learning</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;"></div>
</div>
</div>
<div class="sidebar-sticky sidebar-nav-message">
<div class="bg-light sidebar-module sidebar-module-inset" style="max-height: 80vh; overflow-y: scroll;" id="sidebar-toc">
<p class="h4 text-muted"><a class="link-unstyled" href="#toc">Table of Contents</a></p>
<div class="toc">
<ul>
<li><a href="#functions">Functions</a><ul>
<li><a href="#first-class-objects">First-Class Objects</a></li>
<li><a href="#inner-functions">Inner Functions</a></li>
<li><a href="#returning-functions-from-functions">Returning Functions From Functions</a></li>
</ul>
</li>
<li><a href="#simple-decorators">Simple Decorators</a><ul>
<li><a href="#syntactic-sugar">Syntactic Sugar!</a></li>
<li><a href="#reusing-decorators">Reusing Decorators</a></li>
<li><a href="#decorating-functions-with-arguments">Decorating Functions With Arguments</a></li>
<li><a href="#returning-values-from-decorated-functions">Returning Values From Decorated Functions</a></li>
<li><a href="#who-are-you-really">Who Are You, Really?</a></li>
</ul>
</li>
<li><a href="#a-few-real-world-examples">A Few Real World Examples</a><ul>
<li><a href="#timing-functions">Timing Functions</a></li>
<li><a href="#debugging-code">Debugging Code</a></li>
<li><a href="#slowing-down-code">Slowing Down Code</a></li>
<li><a href="#registering-plugins">Registering Plugins</a></li>
<li><a href="#is-the-user-logged-in">Is the User Logged In?</a></li>
</ul>
</li>
<li><a href="#fancy-decorators">Fancy Decorators</a><ul>
<li><a href="#decorating-classes">Decorating Classes</a></li>
<li><a href="#nesting-decorators">Nesting Decorators</a></li>
<li><a href="#decorators-with-arguments">Decorators With Arguments</a></li>
<li><a href="#both-please-but-never-mind-the-bread">Both Please, But Never Mind the Bread</a></li>
<li><a href="#stateful-decorators">Stateful Decorators</a></li>
<li><a href="#classes-as-decorators">Classes as Decorators</a></li>
</ul>
</li>
<li><a href="#more-real-world-examples">More Real World Examples</a><ul>
<li><a href="#slowing-down-code-revisited">Slowing Down Code, Revisited</a></li>
<li><a href="#creating-singletons">Creating Singletons</a></li>
<li><a href="#caching-return-values">Caching Return Values</a></li>
<li><a href="#adding-information-about-units">Adding Information About Units</a></li>
<li><a href="#validating-json">Validating JSON</a></li>
</ul>
</li>
<li><a href="#conclusion">Conclusion</a></li>
<li><a href="#further-reading">Further Reading</a></li>
</ul>
</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 out this %23Python tutorial: Primer%20on%20Python%20Decorators by @realpython&url=https%3A//realpython.com/primer-on-python-decorators/" class="mr-1 badge badge-twitter text-light mb-1"><i class="mr-1 fa fa-twitter text-light"></i>Tweet</a>
<a target="_blank" rel="nofollow" href="https://facebook.com/sharer/sharer.php?u=https%3A//realpython.com/primer-on-python-decorators/" class="mr-1 badge badge-facebook text-light mb-1"><i class="mr-1 fa fa-facebook text-light"></i>Share</a>
<a target="_blank" rel="nofollow" href="mailto:?subject=Python article for you&body=Check out this Python tutorial:%0A%0APrimer%20on%20Python%20Decorators%0A%0Ahttps%3A//realpython.com/primer-on-python-decorators/" class="mr-1 badge badge-red text-light mb-1"><i class="mr-1 fa fa-envelope text-light"></i>Email</a>
</span>
</div>
<div class="sidebar-module sidebar-module-inset border card">
<p><span class="badge badge-pill badge-success"><i class="fa fa-play-circle mr-1" aria-hidden="true"></i> Recommended Video Course</span><br><a class="stretched-link text-success" href="/courses/python-decorators-101/">Python Decorators 101</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;"></div>
</div>
</div>
</div>
</aside>
</div>
</div>
<div class="modal fade" id="modal-decorators-cheatsheet" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
<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;">
<div class="progress-bar progress-bar-striped progress-bar-animated w-50" role="progressbar"></div>
</div>
</div>
<div class="col-12">
<p 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">&times;</span>
</button>
</div>
<div class="modal-body m-4">
<div class="container-fluid">
<div class="row align-items-center">
<div class="col-12 col-lg-4 mb-4">
<img class="img-fluid rounded" src="https://files.realpython.com/media/Screenshot_2018-08-22_08.53.54.c0da91f23a6a.png" width="762" height="592" srcset="https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-08-22_08.53.54.c0da91f23a6a.png&amp;w=190&amp;sig=fd11e4309a0a7d8a9ad825bc79de6bda4569c888 190w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screenshot_2018-08-22_08.53.54.c0da91f23a6a.png&amp;w=381&amp;sig=8a76902dfe13cd83dc30e1aefe5213bc139bbecd 381w, https://files.realpython.com/media/Screenshot_2018-08-22_08.53.54.c0da91f23a6a.png 762w" sizes="50vw" alt="Python Decorators Cheatsheet">
</div>
<div class="col">
<p class="text-center h3 mb-4">Python Decorators Cheat Sheet (PDF)</p>
<form class="col-12" action="/optins/process/" method="post">
<input type="hidden" name="csrfmiddlewaretoken" value="APHcibHwrBuqt3ULEi1fpqoAksKrzZcjzkcOoObHwIKXPQcmqbJWzjIZjTh8JogZ">
<input type="hidden" name="slug" value="decorators-cheatsheet">
<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">Send Cheat Sheet »</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal fade" id="modal-power-of-decorators-fixed" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
<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;">
<div class="progress-bar progress-bar-striped progress-bar-animated w-50" role="progressbar"></div>
</div>
</div>
<div class="col-12">
<p 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">&times;</span>
</button>
</div>
<div class="modal-body m-4">
<div class="container-fluid">
<div class="row align-items-center">
<div class="col-12 col-lg-4 mb-4">
<img class="img-fluid rounded" src="https://files.realpython.com/media/python-logo.8eb72ea6927b.png" width="1000" height="1000" srcset="https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/python-logo.8eb72ea6927b.png&amp;w=250&amp;sig=3922247197a4a17add6954dd288123a4df30e55a 250w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/python-logo.8eb72ea6927b.png&amp;w=500&amp;sig=40c3b1b474ae94aec528188714ba5878a77362ea 500w, https://files.realpython.com/media/python-logo.8eb72ea6927b.png 1000w" sizes="50vw" alt="Python Logo">
</div>
<div class="col">
<p class="text-center h3 mb-4">The Power of Python Decorators: Advanced Patterns &amp; Techniques (PDF Guide)</p>
<form class="col-12" action="/optins/process/" method="post">
<input type="hidden" name="csrfmiddlewaretoken" value="APHcibHwrBuqt3ULEi1fpqoAksKrzZcjzkcOoObHwIKXPQcmqbJWzjIZjTh8JogZ">
<input type="hidden" name="slug" value="power-of-decorators-fixed">
<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">Get the Python Decorators Guide »</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal fade" id="modal-decorators-qa-2019" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
<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;">
<div class="progress-bar progress-bar-striped progress-bar-animated w-50" role="progressbar"></div>
</div>
</div>
<div class="col-12">
<p 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">&times;</span>
</button>
</div>
<div class="modal-body m-4">
<div class="container-fluid">
<div class="row align-items-center">
<div class="col-12 col-lg-4 mb-4">
<img class="img-fluid rounded" src="https://files.realpython.com/media/Primer-on-Python-Decorators_Watermarked.d0da542fa3fc.jpg" width="1920" height="1080" srcset="https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Primer-on-Python-Decorators_Watermarked.d0da542fa3fc.jpg&amp;w=480&amp;sig=45483afc98ba9e9ed26f1672d17d426e011761df 480w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Primer-on-Python-Decorators_Watermarked.d0da542fa3fc.jpg&amp;w=960&amp;sig=5b4082dbad29a1c1f1fdbe7b5de93af202663c6a 960w, https://files.realpython.com/media/Primer-on-Python-Decorators_Watermarked.d0da542fa3fc.jpg 1920w" sizes="50vw" alt="Python Decorators">
</div>
<div class="col">
<p class="text-center h3 mb-4">Python Decorators Q&amp;A Transcript (PDF)</p>
<form class="col-12" action="/optins/process/" method="post">
<input type="hidden" name="csrfmiddlewaretoken" value="APHcibHwrBuqt3ULEi1fpqoAksKrzZcjzkcOoObHwIKXPQcmqbJWzjIZjTh8JogZ">
<input type="hidden" name="slug" value="decorators-qa-2019">
<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">Send My PDF »</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<footer class="footer">
<div class="container">
<div class="w-75 mx-auto my-0">
<div style="display:block;position:relative;">
<div style="display:block;width:100%;padding-top:12.5%;"></div>
<div class="rpad" data-unit="8x1" style="position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;"></div>
</div>
</div>
<p class="text-center text-muted w-75 mx-auto">© 20122020 Real Python&nbsp;⋅ <a href="/newsletter/">Newsletter</a>&nbsp;⋅ <a href="/podcasts/rpp/">Podcast</a>&nbsp;⋅ <a href="https://www.youtube.com/realpython">YouTube</a>&nbsp;⋅ <a href="https://twitter.com/realpython">Twitter</a>&nbsp;⋅ <a href="https://facebook.com/LearnRealPython">Facebook</a>&nbsp;⋅ <a href="https://www.instagram.com/realpython/">Instagram</a>&nbsp;⋅ <a href="/">Python&nbsp;Tutorials</a>&nbsp;⋅ <a href="/search">Search</a>&nbsp;⋅ <a href="/privacy-policy/">Privacy Policy</a>&nbsp;⋅ <a href="/energy-policy/" class="text-success active">Energy Policy</a>&nbsp;⋅ <a href="/sponsorships/">Advertise</a>&nbsp;⋅ <a href="/contact/">Contact</a><br>❤️ Happy Pythoning!</p>
</div>
</footer>
<script>
(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.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 src="/static/jquery.min.dc5e7f18c8d3.js"></script>
<script src="/static/popper.min.1022eaf388cc.js"></script>
<script src="/static/bootstrap.min.f20fa8b102f2.js"></script>
<script src="/static/repl-toggle.366cb6d72340.js"></script>
<script>window.rp_prop_id = '58946116052';</script>
<script src="https://srv.realpython.net/tag.js" async></script>
<script src="/static/toc-refresh.412f6e102282.js" async></script>
<script id="dsq-count-scr" src="https://realpython.disqus.com/count.js" async></script>
<script>
var disqus_config = function () {
this.page.url = 'https://realpython.com/primer-on-python-decorators/';
this.page.identifier = 'https://realpython.com/primer-on-python-decorators/';
this.callbacks.onReady = [function() {
if (window.onDisqusReady) {
window.onDisqusReady();
}
}];
};
var disqus_script_url = 'https://realpython.disqus.com/embed.js';
</script>
<script src="/static/lazydisqus.3a3e39583b32.js" defer></script>
<script src="/static/articlevc.11f8c3a3f08d.js" defer></script>
<script type="application/ld+json">
{
"@context": "http://schema.org",
"@type": "Article",
"headline": "Primer on Python Decorators",
"image": {
"@type": "ImageObject",
"url": "https://files.realpython.com/media/Primer-on-Python-Decorators_Watermarked.d0da542fa3fc.jpg",
"width": 1920,
"height": 1080
},
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "https://realpython.com/primer-on-python-decorators/"
},
"datePublished": "2018-08-22T16:00:00+00:00",
"dateModified": "2020-12-18T23:55:42.878208+00:00",
"publisher": {
"@type": "Organization",
"name": "Real Python",
"logo": {
"@type": "ImageObject",
"url": "https://realpython.com/static/real-python-logo-square-tiny.b2452b6d3823.png",
"width": 60,
"height": 60
}
},
"author": {
"@type": "Organization",
"name": "Real Python",
"url": "https://realpython.com",
"logo": "https://realpython.com/static/real-python-logo-square.146e987bf77c.png"
},
"description": "In this introductory tutorial, we&#x27;ll look at what Python decorators are and how to create and use them."
}
</script>
<script>
var _dcq = _dcq || [];
var _dcs = _dcs || {};
_dcs.account = '6214500';
(function() {
var dc = document.createElement('script');
dc.type = 'text/javascript'; dc.async = true;
dc.src = '//tag.getdrip.com/6214500.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(dc, s);
})();
</script>
<script type="text/javascript">(function(){window['__CF$cv$params']={r:'6085d4d7d9cc6179',m:'2dbf24d5eea10ee95a641a479e5bc0e51ce81e44-1609101362-1800-AWokc7ylH13jHI1gyNrUE+y/V0pBFC+rJ3uM/qHDnLkKAY2mwS2b2AibOnGUHQEHCLgsURHkxpQOsvs92px2cTZt6+abEOAMPKKSXarkF7bkN+DLxH42JBsqzP89QVsH8xDW5WZmF5rtGphdIlsZSQ8=',s:[0x9e017daa80,0x5627e559d8],}})();</script></body>
</html>
"""
return html
def test_referencia_instructions_extract_correctly(real_ad_html):
soup = BeautifulSoup(real_ad_html, "html5lib")
referencia_instructions = ReferenciaFieldInstructions()
referencia_instructions.scrape(soup)
referencia_instructions.validate()
assert (
referencia_instructions.found is True
and referencia_instructions.valid is True
and referencia_instructions.value is not None
and referencia_instructions.search_issue is None
)
def test_referencia_instructions_find_nothing_in_unrelated_html(unrelated_html):
soup = BeautifulSoup(unrelated_html, "html5lib")
referencia_instructions = ReferenciaFieldInstructions()
referencia_instructions.scrape(soup)
referencia_instructions.validate()
assert (
referencia_instructions.found is False
and referencia_instructions.valid is None
and referencia_instructions.value is None
and referencia_instructions.search_issue is not None
)
def test_all_instructions_extract_correctly(real_ad_html):
soup = BeautifulSoup(real_ad_html, "html5lib")
all_instructions = [
ReferenciaFieldInstructions(),
PrecioFieldInstructions(),
TamanoCategoricoFieldInstructions(),
M2FieldInstructions(),
TipoAnuncioFieldInstructions(),
CalleFieldInstructions(),
BarrioFieldInstructions(),
DistritoFieldInstructions(),
CiudadFieldInstructions(),
SecondaryFeaturesFieldInstructions(
field_name="cubierta", search_keyword="Cubierta"
),
SecondaryFeaturesFieldInstructions(
field_name="puerta_auto", search_keyword="Puerta"
),
SecondaryFeaturesFieldInstructions(
field_name="ascensor", search_keyword="ascensor"
),
SecondaryFeaturesFieldInstructions(
field_name="alarma", search_keyword="Alarma"
),
SecondaryFeaturesFieldInstructions(
field_name="circuito", search_keyword="Cámaras"
),
SecondaryFeaturesFieldInstructions(
field_name="personal", search_keyword="Personal"
),
TelefonoFieldInstructions(),
]
for instruction in all_instructions:
instruction.scrape(soup).validate()
assert all(
[
instruction.found is True
and instruction.valid is True
and instruction.value is not None
and instruction.search_issue is None
for instruction in all_instructions
]
)
def test_all_instructions_fail_on_unrelated_html(unrelated_html):
soup = BeautifulSoup(unrelated_html, "html5lib")
all_instructions = [
ReferenciaFieldInstructions(),
PrecioFieldInstructions(),
TamanoCategoricoFieldInstructions(),
M2FieldInstructions(),
TipoAnuncioFieldInstructions(),
CalleFieldInstructions(),
BarrioFieldInstructions(),
DistritoFieldInstructions(),
CiudadFieldInstructions(),
SecondaryFeaturesFieldInstructions(
field_name="cubierta", search_keyword="Cubierta"
),
SecondaryFeaturesFieldInstructions(
field_name="puerta_auto", search_keyword="Puerta"
),
SecondaryFeaturesFieldInstructions(
field_name="ascensor", search_keyword="ascensor"
),
SecondaryFeaturesFieldInstructions(
field_name="alarma", search_keyword="Alarma"
),
SecondaryFeaturesFieldInstructions(
field_name="circuito", search_keyword="Cámaras"
),
SecondaryFeaturesFieldInstructions(
field_name="personal", search_keyword="Personal"
),
TelefonoFieldInstructions(),
]
for instruction in all_instructions:
instruction.scrape(soup).validate()
assert all(
[
instruction.found is False
and (instruction.valid is False or instruction.valid is None)
and instruction.value is None
for instruction in all_instructions
]
)
def test_parsing_flow_works_for_ad_html(real_ad_html):
soup = BeautifulSoup(real_ad_html, "html5lib")
parsing_flow = ParsingFlow()
all_instructions = [
ReferenciaFieldInstructions(),
PrecioFieldInstructions(),
TamanoCategoricoFieldInstructions(),
M2FieldInstructions(),
TipoAnuncioFieldInstructions(),
CalleFieldInstructions(),
BarrioFieldInstructions(),
DistritoFieldInstructions(),
CiudadFieldInstructions(),
SecondaryFeaturesFieldInstructions(
field_name="cubierta", search_keyword="Cubierta"
),
SecondaryFeaturesFieldInstructions(
field_name="puerta_auto", search_keyword="Puerta"
),
SecondaryFeaturesFieldInstructions(
field_name="ascensor", search_keyword="ascensor"
),
SecondaryFeaturesFieldInstructions(
field_name="alarma", search_keyword="Alarma"
),
SecondaryFeaturesFieldInstructions(
field_name="circuito", search_keyword="Cámaras"
),
SecondaryFeaturesFieldInstructions(
field_name="personal", search_keyword="Personal"
),
TelefonoFieldInstructions(),
]
parsing_flow.add_instructions(all_instructions)
parsing_flow.execute_flow(soup)
assert (
parsing_flow.all_non_optional_fields_were_found
and parsing_flow.all_found_fields_are_valid
and len(parsing_flow.field_values) == len(all_instructions)
)
def test_parsing_flow_fails_for_unrelated_html(unrelated_html):
soup = BeautifulSoup(unrelated_html, "html5lib")
parsing_flow = ParsingFlow()
all_instructions = [
ReferenciaFieldInstructions(),
PrecioFieldInstructions(),
TamanoCategoricoFieldInstructions(),
M2FieldInstructions(),
TipoAnuncioFieldInstructions(),
CalleFieldInstructions(),
BarrioFieldInstructions(),
DistritoFieldInstructions(),
CiudadFieldInstructions(),
SecondaryFeaturesFieldInstructions(
field_name="cubierta", search_keyword="Cubierta"
),
SecondaryFeaturesFieldInstructions(
field_name="puerta_auto", search_keyword="Puerta"
),
SecondaryFeaturesFieldInstructions(
field_name="ascensor", search_keyword="ascensor"
),
SecondaryFeaturesFieldInstructions(
field_name="alarma", search_keyword="Alarma"
),
SecondaryFeaturesFieldInstructions(
field_name="circuito", search_keyword="Cámaras"
),
SecondaryFeaturesFieldInstructions(
field_name="personal", search_keyword="Personal"
),
TelefonoFieldInstructions(),
]
parsing_flow.add_instructions(all_instructions)
parsing_flow.execute_flow(soup)
assert not parsing_flow.all_non_optional_fields_were_found and len(
parsing_flow.issues
) == len([field for field in all_instructions if not field.is_optional])
def test_parsing_flow_generator_returns_proper_flows():
four_instructions_with_params = (
(ReferenciaFieldInstructions, {}),
(PrecioFieldInstructions, {}),
(TamanoCategoricoFieldInstructions, {}),
(
SecondaryFeaturesFieldInstructions,
{"field_name": "personal", "search_keyword": "Personal"},
),
)
parsing_flow_generator = ParsingFlowGenerator(
parsing_flow_class=ParsingFlow,
instructions_to_attach_with_params=four_instructions_with_params,
)
a_new_parsing_flow = parsing_flow_generator.get_new_flow()
assert (
isinstance(a_new_parsing_flow, ParsingFlow),
len(a_new_parsing_flow._instructions) == len(four_instructions_with_params),
all([field.found is None for field in a_new_parsing_flow._instructions]),
all([field.valid is None for field in a_new_parsing_flow._instructions]),
)