[{"data":1,"prerenderedAt":1011},["ShallowReactive",2],{"blog-\u002Fblog\u002Fpython-methods-properties-explained":3},{"id":4,"title":5,"body":6,"description":997,"difficulty":998,"extension":999,"framework":1000,"frameworkSlug":48,"meta":1001,"navigation":88,"order":92,"path":1002,"qaPath":1003,"seo":1004,"stem":1005,"subtopic":1006,"topic":1007,"topicSlug":1008,"updated":1009,"__hash__":1010},"blog\u002Fblog\u002Fpython-methods-properties-explained.md","Python Methods and Properties Explained — staticmethod, classmethod, and @property",{"type":7,"value":8,"toc":988},"minimark",[9,14,36,40,43,268,300,304,313,430,445,449,454,571,581,585,588,761,765,780,930,941,945,984],[10,11,13],"h2",{"id":12},"python-methods-and-properties-explained","Python methods and properties, explained",[15,16,17,18,22,23,26,27,30,31,35],"p",{},"Python has three kinds of methods and a clean way to turn methods into attribute-like access.\nKnowing when to use ",[19,20,21],"code",{},"@classmethod",", ",[19,24,25],{},"@staticmethod",", and ",[19,28,29],{},"@property"," — and ",[32,33,34],"em",{},"why"," Python\nprefers properties over Java-style getters — is a common interview signal.",[10,37,39],{"id":38},"three-kinds-of-methods","Three kinds of methods",[15,41,42],{},"The difference is what (if anything) gets passed as the first argument:",[44,45,50],"pre",{"className":46,"code":47,"language":48,"meta":49,"style":49},"language-python shiki shiki-themes github-light github-dark","class Pizza:\n    DEFAULT_SIZE = \"medium\"\n\n    def __init__(self, toppings):\n        self.toppings = toppings\n\n    def describe(self):                  # instance method — gets self\n        return f\"Pizza with {self.toppings}\"\n\n    @classmethod\n    def margherita(cls):                 # classmethod — gets the class\n        return cls([\"tomato\", \"mozzarella\"])\n\n    @staticmethod\n    def is_valid_size(size):             # staticmethod — gets nothing special\n        return size in {\"small\", \"medium\", \"large\"}\n","python","",[19,51,52,69,83,90,102,117,122,137,161,166,175,189,211,216,224,238],{"__ignoreMap":49},[53,54,57,61,65],"span",{"class":55,"line":56},"line",1,[53,58,60],{"class":59},"szBVR","class",[53,62,64],{"class":63},"sScJk"," Pizza",[53,66,68],{"class":67},"sVt8B",":\n",[53,70,72,76,79],{"class":55,"line":71},2,[53,73,75],{"class":74},"sj4cs","    DEFAULT_SIZE",[53,77,78],{"class":59}," =",[53,80,82],{"class":81},"sZZnC"," \"medium\"\n",[53,84,86],{"class":55,"line":85},3,[53,87,89],{"emptyLinePlaceholder":88},true,"\n",[53,91,93,96,99],{"class":55,"line":92},4,[53,94,95],{"class":59},"    def",[53,97,98],{"class":74}," __init__",[53,100,101],{"class":67},"(self, toppings):\n",[53,103,105,108,111,114],{"class":55,"line":104},5,[53,106,107],{"class":74},"        self",[53,109,110],{"class":67},".toppings ",[53,112,113],{"class":59},"=",[53,115,116],{"class":67}," toppings\n",[53,118,120],{"class":55,"line":119},6,[53,121,89],{"emptyLinePlaceholder":88},[53,123,125,127,130,133],{"class":55,"line":124},7,[53,126,95],{"class":59},[53,128,129],{"class":63}," describe",[53,131,132],{"class":67},"(self):                  ",[53,134,136],{"class":135},"sJ8bj","# instance method — gets self\n",[53,138,140,143,146,149,152,155,158],{"class":55,"line":139},8,[53,141,142],{"class":59},"        return",[53,144,145],{"class":59}," f",[53,147,148],{"class":81},"\"Pizza with ",[53,150,151],{"class":74},"{self",[53,153,154],{"class":67},".toppings",[53,156,157],{"class":74},"}",[53,159,160],{"class":81},"\"\n",[53,162,164],{"class":55,"line":163},9,[53,165,89],{"emptyLinePlaceholder":88},[53,167,169,172],{"class":55,"line":168},10,[53,170,171],{"class":63},"    @",[53,173,174],{"class":74},"classmethod\n",[53,176,178,180,183,186],{"class":55,"line":177},11,[53,179,95],{"class":59},[53,181,182],{"class":63}," margherita",[53,184,185],{"class":67},"(cls):                 ",[53,187,188],{"class":135},"# classmethod — gets the class\n",[53,190,192,194,197,200,203,205,208],{"class":55,"line":191},12,[53,193,142],{"class":59},[53,195,196],{"class":74}," cls",[53,198,199],{"class":67},"([",[53,201,202],{"class":81},"\"tomato\"",[53,204,22],{"class":67},[53,206,207],{"class":81},"\"mozzarella\"",[53,209,210],{"class":67},"])\n",[53,212,214],{"class":55,"line":213},13,[53,215,89],{"emptyLinePlaceholder":88},[53,217,219,221],{"class":55,"line":218},14,[53,220,171],{"class":63},[53,222,223],{"class":74},"staticmethod\n",[53,225,227,229,232,235],{"class":55,"line":226},15,[53,228,95],{"class":59},[53,230,231],{"class":63}," is_valid_size",[53,233,234],{"class":67},"(size):             ",[53,236,237],{"class":135},"# staticmethod — gets nothing special\n",[53,239,241,243,246,249,252,255,257,260,262,265],{"class":55,"line":240},16,[53,242,142],{"class":59},[53,244,245],{"class":67}," size ",[53,247,248],{"class":59},"in",[53,250,251],{"class":67}," {",[53,253,254],{"class":81},"\"small\"",[53,256,22],{"class":67},[53,258,259],{"class":81},"\"medium\"",[53,261,22],{"class":67},[53,263,264],{"class":81},"\"large\"",[53,266,267],{"class":67},"}\n",[269,270,271,284,294],"ul",{},[272,273,274,275,279,280,283],"li",{},"An ",[276,277,278],"strong",{},"instance method"," receives ",[19,281,282],{},"self"," and works with object state.",[272,285,286,287,279,290,293],{},"A ",[276,288,289],{},"classmethod",[19,291,292],{},"cls"," (the class) and works with the class itself.",[272,295,286,296,299],{},[276,297,298],{},"staticmethod"," receives neither — it's a plain function namespaced under the class.",[10,301,303],{"id":302},"classmethod-as-an-alternative-constructor","classmethod as an alternative constructor",[15,305,306,307,309,310,312],{},"The most common use of ",[19,308,21],{}," is a named alternative constructor. Because it\nreceives ",[19,311,292],{},", it works correctly with subclasses too:",[44,314,316],{"className":46,"code":315,"language":48,"meta":49,"style":49},"class Date:\n    def __init__(self, year, month, day):\n        self.year, self.month, self.day = year, month, day\n\n    @classmethod\n    def from_string(cls, s):\n        y, m, d = map(int, s.split(\"-\"))\n        return cls(y, m, d)          # cls, not Date — subclass-friendly\n\nDate.from_string(\"2026-06-19\")\n",[19,317,318,327,336,358,362,368,378,403,415,419],{"__ignoreMap":49},[53,319,320,322,325],{"class":55,"line":56},[53,321,60],{"class":59},[53,323,324],{"class":63}," Date",[53,326,68],{"class":67},[53,328,329,331,333],{"class":55,"line":71},[53,330,95],{"class":59},[53,332,98],{"class":74},[53,334,335],{"class":67},"(self, year, month, day):\n",[53,337,338,340,343,345,348,350,353,355],{"class":55,"line":85},[53,339,107],{"class":74},[53,341,342],{"class":67},".year, ",[53,344,282],{"class":74},[53,346,347],{"class":67},".month, ",[53,349,282],{"class":74},[53,351,352],{"class":67},".day ",[53,354,113],{"class":59},[53,356,357],{"class":67}," year, month, day\n",[53,359,360],{"class":55,"line":92},[53,361,89],{"emptyLinePlaceholder":88},[53,363,364,366],{"class":55,"line":104},[53,365,171],{"class":63},[53,367,174],{"class":74},[53,369,370,372,375],{"class":55,"line":119},[53,371,95],{"class":59},[53,373,374],{"class":63}," from_string",[53,376,377],{"class":67},"(cls, s):\n",[53,379,380,383,385,388,391,394,397,400],{"class":55,"line":124},[53,381,382],{"class":67},"        y, m, d ",[53,384,113],{"class":59},[53,386,387],{"class":74}," map",[53,389,390],{"class":67},"(",[53,392,393],{"class":74},"int",[53,395,396],{"class":67},", s.split(",[53,398,399],{"class":81},"\"-\"",[53,401,402],{"class":67},"))\n",[53,404,405,407,409,412],{"class":55,"line":139},[53,406,142],{"class":59},[53,408,196],{"class":74},[53,410,411],{"class":67},"(y, m, d)          ",[53,413,414],{"class":135},"# cls, not Date — subclass-friendly\n",[53,416,417],{"class":55,"line":163},[53,418,89],{"emptyLinePlaceholder":88},[53,420,421,424,427],{"class":55,"line":168},[53,422,423],{"class":67},"Date.from_string(",[53,425,426],{"class":81},"\"2026-06-19\"",[53,428,429],{"class":67},")\n",[15,431,432,433,436,437,440,441,444],{},"Using ",[19,434,435],{},"cls(...)"," rather than ",[19,438,439],{},"Date(...)"," means a subclass's ",[19,442,443],{},"from_string"," returns the\nsubclass, not the base.",[10,446,448],{"id":447},"property-methods-that-look-like-attributes","@property — methods that look like attributes",[15,450,451,453],{},[19,452,29],{}," turns a method into a computed, read-only attribute accessed without parentheses:",[44,455,457],{"className":46,"code":456,"language":48,"meta":49,"style":49},"class Circle:\n    def __init__(self, radius):\n        self.radius = radius\n\n    @property\n    def area(self):\n        return 3.14159 * self.radius ** 2\n\nc = Circle(2)\nc.area        # 12.566...  — no parentheses; computed on access\nc.area = 10   # AttributeError — read-only by default\n",[19,458,459,468,477,489,493,500,510,531,535,550,558],{"__ignoreMap":49},[53,460,461,463,466],{"class":55,"line":56},[53,462,60],{"class":59},[53,464,465],{"class":63}," Circle",[53,467,68],{"class":67},[53,469,470,472,474],{"class":55,"line":71},[53,471,95],{"class":59},[53,473,98],{"class":74},[53,475,476],{"class":67},"(self, radius):\n",[53,478,479,481,484,486],{"class":55,"line":85},[53,480,107],{"class":74},[53,482,483],{"class":67},".radius ",[53,485,113],{"class":59},[53,487,488],{"class":67}," radius\n",[53,490,491],{"class":55,"line":92},[53,492,89],{"emptyLinePlaceholder":88},[53,494,495,497],{"class":55,"line":104},[53,496,171],{"class":63},[53,498,499],{"class":74},"property\n",[53,501,502,504,507],{"class":55,"line":119},[53,503,95],{"class":59},[53,505,506],{"class":63}," area",[53,508,509],{"class":67},"(self):\n",[53,511,512,514,517,520,523,525,528],{"class":55,"line":124},[53,513,142],{"class":59},[53,515,516],{"class":74}," 3.14159",[53,518,519],{"class":59}," *",[53,521,522],{"class":74}," self",[53,524,483],{"class":67},[53,526,527],{"class":59},"**",[53,529,530],{"class":74}," 2\n",[53,532,533],{"class":55,"line":139},[53,534,89],{"emptyLinePlaceholder":88},[53,536,537,540,542,545,548],{"class":55,"line":163},[53,538,539],{"class":67},"c ",[53,541,113],{"class":59},[53,543,544],{"class":67}," Circle(",[53,546,547],{"class":74},"2",[53,549,429],{"class":67},[53,551,552,555],{"class":55,"line":168},[53,553,554],{"class":67},"c.area        ",[53,556,557],{"class":135},"# 12.566...  — no parentheses; computed on access\n",[53,559,560,563,565,568],{"class":55,"line":177},[53,561,562],{"class":67},"c.area ",[53,564,113],{"class":59},[53,566,567],{"class":74}," 10",[53,569,570],{"class":135},"   # AttributeError — read-only by default\n",[15,572,573,576,577,580],{},[19,574,575],{},"area"," is always derived from ",[19,578,579],{},"radius",", so it can never go stale.",[10,582,584],{"id":583},"adding-a-setter-with-validation","Adding a setter with validation",[15,586,587],{},"A property can have a setter, letting you run validation while keeping plain attribute syntax:",[44,589,591],{"className":46,"code":590,"language":48,"meta":49,"style":49},"class Temperature:\n    def __init__(self, celsius):\n        self._celsius = celsius\n\n    @property\n    def celsius(self):\n        return self._celsius\n\n    @celsius.setter\n    def celsius(self, value):\n        if value \u003C -273.15:\n            raise ValueError(\"below absolute zero\")\n        self._celsius = value\n\nt = Temperature(20)\nt.celsius = 25       # runs the setter (validates)\nt.celsius = -300     # ValueError\n",[19,592,593,602,611,623,627,633,642,651,655,660,669,688,703,714,718,733,746],{"__ignoreMap":49},[53,594,595,597,600],{"class":55,"line":56},[53,596,60],{"class":59},[53,598,599],{"class":63}," Temperature",[53,601,68],{"class":67},[53,603,604,606,608],{"class":55,"line":71},[53,605,95],{"class":59},[53,607,98],{"class":74},[53,609,610],{"class":67},"(self, celsius):\n",[53,612,613,615,618,620],{"class":55,"line":85},[53,614,107],{"class":74},[53,616,617],{"class":67},"._celsius ",[53,619,113],{"class":59},[53,621,622],{"class":67}," celsius\n",[53,624,625],{"class":55,"line":92},[53,626,89],{"emptyLinePlaceholder":88},[53,628,629,631],{"class":55,"line":104},[53,630,171],{"class":63},[53,632,499],{"class":74},[53,634,635,637,640],{"class":55,"line":119},[53,636,95],{"class":59},[53,638,639],{"class":63}," celsius",[53,641,509],{"class":67},[53,643,644,646,648],{"class":55,"line":124},[53,645,142],{"class":59},[53,647,522],{"class":74},[53,649,650],{"class":67},"._celsius\n",[53,652,653],{"class":55,"line":139},[53,654,89],{"emptyLinePlaceholder":88},[53,656,657],{"class":55,"line":163},[53,658,659],{"class":63},"    @celsius.setter\n",[53,661,662,664,666],{"class":55,"line":168},[53,663,95],{"class":59},[53,665,639],{"class":63},[53,667,668],{"class":67},"(self, value):\n",[53,670,671,674,677,680,683,686],{"class":55,"line":177},[53,672,673],{"class":59},"        if",[53,675,676],{"class":67}," value ",[53,678,679],{"class":59},"\u003C",[53,681,682],{"class":59}," -",[53,684,685],{"class":74},"273.15",[53,687,68],{"class":67},[53,689,690,693,696,698,701],{"class":55,"line":191},[53,691,692],{"class":59},"            raise",[53,694,695],{"class":74}," ValueError",[53,697,390],{"class":67},[53,699,700],{"class":81},"\"below absolute zero\"",[53,702,429],{"class":67},[53,704,705,707,709,711],{"class":55,"line":213},[53,706,107],{"class":74},[53,708,617],{"class":67},[53,710,113],{"class":59},[53,712,713],{"class":67}," value\n",[53,715,716],{"class":55,"line":218},[53,717,89],{"emptyLinePlaceholder":88},[53,719,720,723,725,728,731],{"class":55,"line":226},[53,721,722],{"class":67},"t ",[53,724,113],{"class":59},[53,726,727],{"class":67}," Temperature(",[53,729,730],{"class":74},"20",[53,732,429],{"class":67},[53,734,735,738,740,743],{"class":55,"line":240},[53,736,737],{"class":67},"t.celsius ",[53,739,113],{"class":59},[53,741,742],{"class":74}," 25",[53,744,745],{"class":135},"       # runs the setter (validates)\n",[53,747,749,751,753,755,758],{"class":55,"line":748},17,[53,750,737],{"class":67},[53,752,113],{"class":59},[53,754,682],{"class":59},[53,756,757],{"class":74},"300",[53,759,760],{"class":135},"     # ValueError\n",[10,762,764],{"id":763},"why-properties-beat-java-style-getters","Why properties beat Java-style getters",[15,766,767,768,771,772,775,776,779],{},"In Java you write ",[19,769,770],{},"getX()","\u002F",[19,773,774],{},"setX()"," from the start \"in case\" you need validation later. In\nPython you just expose a plain attribute and ",[276,777,778],{},"upgrade it to a property later"," without\nchanging the public API:",[44,781,783],{"className":46,"code":782,"language":48,"meta":49,"style":49},"# Start simple:\nclass User:\n    def __init__(self, name):\n        self.name = name          # public attribute\n\n# Later, add validation transparently — callers still write user.name:\nclass User:\n    def __init__(self, name):\n        self.name = name\n    @property\n    def name(self):\n        return self._name\n    @name.setter\n    def name(self, value):\n        if not value:\n            raise ValueError(\"name required\")\n        self._name = value\n",[19,784,785,790,799,808,823,827,832,840,848,859,865,874,883,888,896,906,919],{"__ignoreMap":49},[53,786,787],{"class":55,"line":56},[53,788,789],{"class":135},"# Start simple:\n",[53,791,792,794,797],{"class":55,"line":71},[53,793,60],{"class":59},[53,795,796],{"class":63}," User",[53,798,68],{"class":67},[53,800,801,803,805],{"class":55,"line":85},[53,802,95],{"class":59},[53,804,98],{"class":74},[53,806,807],{"class":67},"(self, name):\n",[53,809,810,812,815,817,820],{"class":55,"line":92},[53,811,107],{"class":74},[53,813,814],{"class":67},".name ",[53,816,113],{"class":59},[53,818,819],{"class":67}," name          ",[53,821,822],{"class":135},"# public attribute\n",[53,824,825],{"class":55,"line":104},[53,826,89],{"emptyLinePlaceholder":88},[53,828,829],{"class":55,"line":119},[53,830,831],{"class":135},"# Later, add validation transparently — callers still write user.name:\n",[53,833,834,836,838],{"class":55,"line":124},[53,835,60],{"class":59},[53,837,796],{"class":63},[53,839,68],{"class":67},[53,841,842,844,846],{"class":55,"line":139},[53,843,95],{"class":59},[53,845,98],{"class":74},[53,847,807],{"class":67},[53,849,850,852,854,856],{"class":55,"line":163},[53,851,107],{"class":74},[53,853,814],{"class":67},[53,855,113],{"class":59},[53,857,858],{"class":67}," name\n",[53,860,861,863],{"class":55,"line":168},[53,862,171],{"class":63},[53,864,499],{"class":74},[53,866,867,869,872],{"class":55,"line":177},[53,868,95],{"class":59},[53,870,871],{"class":63}," name",[53,873,509],{"class":67},[53,875,876,878,880],{"class":55,"line":191},[53,877,142],{"class":59},[53,879,522],{"class":74},[53,881,882],{"class":67},"._name\n",[53,884,885],{"class":55,"line":213},[53,886,887],{"class":63},"    @name.setter\n",[53,889,890,892,894],{"class":55,"line":218},[53,891,95],{"class":59},[53,893,871],{"class":63},[53,895,668],{"class":67},[53,897,898,900,903],{"class":55,"line":226},[53,899,673],{"class":59},[53,901,902],{"class":59}," not",[53,904,905],{"class":67}," value:\n",[53,907,908,910,912,914,917],{"class":55,"line":240},[53,909,692],{"class":59},[53,911,695],{"class":74},[53,913,390],{"class":67},[53,915,916],{"class":81},"\"name required\"",[53,918,429],{"class":67},[53,920,921,923,926,928],{"class":55,"line":748},[53,922,107],{"class":74},[53,924,925],{"class":67},"._name ",[53,927,113],{"class":59},[53,929,713],{"class":67},[15,931,932,933,936,937,940],{},"Callers never need to change from ",[19,934,935],{},"user.name"," to ",[19,938,939],{},"user.getName()"," — that's the payoff.",[10,942,944],{"id":943},"recap","Recap",[15,946,947,950,951,953,954,950,957,959,960,963,964,966,967,970,971,975,976,979,980,983],{},[276,948,949],{},"Instance methods"," take ",[19,952,282],{}," and use object state; ",[276,955,956],{},"classmethods",[19,958,292],{}," and are\nideal for ",[276,961,962],{},"alternative constructors"," that stay subclass-friendly via ",[19,965,435],{},";\n",[276,968,969],{},"staticmethods"," take nothing special and just live in the class namespace. ",[276,972,973],{},[19,974,29],{},"\nexposes computed or read-only attributes with plain ",[19,977,978],{},"obj.attr"," syntax, and a paired\n",[19,981,982],{},"@x.setter"," adds validation. Because of properties, Python code starts with public\nattributes and adds getters\u002Fsetters later without breaking callers — so you never write\nJava-style boilerplate up front.",[985,986,987],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":49,"searchDepth":71,"depth":71,"links":989},[990,991,992,993,994,995,996],{"id":12,"depth":71,"text":13},{"id":38,"depth":71,"text":39},{"id":302,"depth":71,"text":303},{"id":447,"depth":71,"text":448},{"id":583,"depth":71,"text":584},{"id":763,"depth":71,"text":764},{"id":943,"depth":71,"text":944},"The difference between instance methods, classmethods, and staticmethods, how classmethods make alternative constructors, and how @property gives computed and read-only attributes.","medium","md","Python",{},"\u002Fblog\u002Fpython-methods-properties-explained","\u002Fpython\u002Foop\u002Fmethods-properties",{"title":5,"description":997},"blog\u002Fpython-methods-properties-explained","Methods & Properties","Object-Oriented Programming","oop","2026-06-19","75to66RL0A4NfI9AQL5SWJITKZP64LpTS4c5MWA1e30",1782244093869]