type is a metaclass, of which classes are instances. Just as an ordinary object is an instance of a class, any new-style class in Python, and thus any class in ...
Start Here
LearnPython
PythonTutorials→In-deptharticlesandvideocourses
LearningPaths→Guidedstudyplansforacceleratedlearning
Quizzes→Checkyourlearningprogress
BrowseTopics→Focusonaspecificareaorskilllevel
CommunityChat→LearnwithotherPythonistas
OfficeHours→LiveQ&AcallswithPythonexperts
Podcast→Hearwhat’snewintheworldofPython
Books→Roundoutyourknowledgeandlearnoffline
UnlockAllContent→
More
PythonLearningResources
PythonNewsletter
PythonJobBoard
MeettheTeam
BecomeaTutorialAuthor
BecomeaVideoInstructor
Search
Join
Sign‑In
PythonMetaclasses
byJohnSturtz
advanced
python
MarkasCompleted
Tweet
Share
Email
TableofContents
Old-Stylevs.New-StyleClasses
Old-StyleClasses
New-StyleClasses
TypeandClass
DefiningaClassDynamically
Example1
Example2
Example3
Example4
CustomMetaclasses
IsThisReallyNecessary?
Conclusion
Removeads
Thetermmetaprogrammingreferstothepotentialforaprogramtohaveknowledgeoformanipulateitself.Pythonsupportsaformofmetaprogrammingforclassescalledmetaclasses.
MetaclassesareanesotericOOPconcept,lurkingbehindvirtuallyallPythoncode.Youareusingthemwhetheryouareawareofitornot.Forthemostpart,youdon’tneedtobeawareofit.MostPythonprogrammersrarely,ifever,havetothinkaboutmetaclasses.
Whentheneedarises,however,Pythonprovidesacapabilitythatnotallobject-orientedlanguagessupport:youcangetunderthehoodanddefinecustommetaclasses.Theuseofcustommetaclassesissomewhatcontroversial,assuggestedbythefollowingquotefromTimPeters,thePythonguruwhoauthoredtheZenofPython:
“Metaclassesaredeepermagicthan99%ofusersshouldeverworryabout.Ifyouwonderwhetheryouneedthem,youdon’t(thepeoplewhoactuallyneedthemknowwithcertaintythattheyneedthem,anddon’tneedanexplanationaboutwhy).”
—TimPeters
TherearePythonistas(asPythonaficionadosareknown)whobelievethatyoushouldneverusecustommetaclasses.Thatmightbegoingabitfar,butitisprobablytruethatcustommetaclassesmostlyaren’tnecessary.Ifitisn’tprettyobviousthataproblemcallsforthem,thenitwillprobablybecleanerandmorereadableifsolvedinasimplerway.
Still,understandingPythonmetaclassesisworthwhile,becauseitleadstoabetterunderstandingoftheinternalsofPythonclassesingeneral.Youneverknow:youmayonedayfindyourselfinoneofthosesituationswhereyoujustknowthatacustommetaclassiswhatyouwant.
GetNotified:Don’tmissthefollowuptothistutorial—ClickheretojointheRealPythonNewsletterandyou’llknowwhenthenextinstallmentcomesout.
Old-Stylevs.New-StyleClasses
InthePythonrealm,aclasscanbeoneoftwovarieties.Noofficialterminologyhasbeendecidedon,sotheyareinformallyreferredtoasold-styleandnew-styleclasses.
RemoveadsOld-StyleClasses
Withold-styleclasses,classandtypearenotquitethesamething.Aninstanceofanold-styleclassisalwaysimplementedfromasinglebuilt-intypecalledinstance.Ifobjisaninstanceofanold-styleclass,obj.__class__designatestheclass,buttype(obj)isalwaysinstance.ThefollowingexampleistakenfromPython2.7:
>>>>>>classFoo:
...pass
...
>>>x=Foo()
>>>x.__class__
>>>type(x)
New-StyleClasses
New-styleclassesunifytheconceptsofclassandtype.Ifobjisaninstanceofanew-styleclass,type(obj)isthesameasobj.__class__:
>>>>>>classFoo:
...pass
>>>obj=Foo()
>>>obj.__class__
>>>type(obj)
>>>obj.__class__istype(obj)
True
>>>>>>n=5
>>>d={'x':1,'y':2}
>>>classFoo:
...pass
...
>>>x=Foo()
>>>forobjin(n,d,x):
...print(type(obj)isobj.__class__)
...
True
True
True
TypeandClass
InPython3,allclassesarenew-styleclasses.Thus,inPython3itisreasonabletorefertoanobject’stypeanditsclassinterchangeably.
Note:InPython2,classesareold-stylebydefault.PriortoPython2.2,new-styleclassesweren’tsupportedatall.FromPython2.2onward,theycanbecreatedbutmustbeexplicitlydeclaredasnew-style.
Rememberthat,inPython,everythingisanobject.Classesareobjectsaswell.Asaresult,aclassmusthaveatype.Whatisthetypeofaclass?
Considerthefollowing:
>>>>>>classFoo:
...pass
...
>>>x=Foo()
>>>type(x)
>>>type(Foo)
ThetypeofxisclassFoo,asyouwouldexpect.ButthetypeofFoo,theclassitself,istype.Ingeneral,thetypeofanynew-styleclassistype.
Thetypeofthebuilt-inclassesyouarefamiliarwithisalsotype:
>>>>>>fortinint,float,dict,list,tuple:
...print(type(t))
...
Forthatmatter,thetypeoftypeistypeaswell(yes,really):
>>>>>>type(type)
typeisametaclass,ofwhichclassesareinstances.Justasanordinaryobjectisaninstanceofaclass,anynew-styleclassinPython,andthusanyclassinPython3,isaninstanceofthetypemetaclass.
Intheabovecase:
xisaninstanceofclassFoo.
Fooisaninstanceofthetypemetaclass.
typeisalsoaninstanceofthetypemetaclass,soitisaninstanceofitself.
RemoveadsDefiningaClassDynamically
Thebuilt-intype()function,whenpassedoneargument,returnsthetypeofanobject.Fornew-styleclasses,thatisgenerallythesameastheobject’s__class__attribute:
>>>>>>type(3)
>>>type(['foo','bar','baz'])
>>>t=(1,2,3,4,5)
>>>type(t)
>>>classFoo:
...pass
...
>>>type(Foo())
Youcanalsocalltype()withthreearguments—type(,,):
specifiestheclassname.Thisbecomesthe__name__attributeoftheclass.
specifiesatupleofthebaseclassesfromwhichtheclassinherits.Thisbecomesthe__bases__attributeoftheclass.
specifiesanamespacedictionarycontainingdefinitionsfortheclassbody.Thisbecomesthe__dict__attributeoftheclass.
Callingtype()inthismannercreatesanewinstanceofthetypemetaclass.Inotherwords,itdynamicallycreatesanewclass.
Ineachofthefollowingexamples,thetopsnippetdefinesaclassdynamicallywithtype(),whilethesnippetbelowitdefinestheclasstheusualway,withtheclassstatement.Ineachcase,thetwosnippetsarefunctionallyequivalent.
Example1
Inthisfirstexample,theandargumentspassedtotype()arebothempty.Noinheritancefromanyparentclassisspecified,andnothingisinitiallyplacedinthenamespacedictionary.Thisisthesimplestclassdefinitionpossible:
>>>>>>Foo=type('Foo',(),{})
>>>x=Foo()
>>>x
<__main__.fooobjectat0x04cfad50>
>>>>>>classFoo:
...pass
...
>>>x=Foo()
>>>x
<__main__.fooobjectat0x0370ad50>
Example2
Here,isatuplewithasingleelementFoo,specifyingtheparentclassthatBarinheritsfrom.Anattribute,attr,isinitiallyplacedintothenamespacedictionary:
>>>>>>Bar=type('Bar',(Foo,),dict(attr=100))
>>>x=Bar()
>>>x.attr
100
>>>x.__class__
>>>x.__class__.__bases__
(,)
>>>>>>classBar(Foo):
...attr=100
...
>>>x=Bar()
>>>x.attr
100
>>>x.__class__
>>>x.__class__.__bases__
(,)
Example3
Thistime,isagainempty.Twoobjectsareplacedintothenamespacedictionaryviatheargument.Thefirstisanattributenamedattrandthesecondafunctionnamedattr_val,whichbecomesamethodofthedefinedclass:
>>>>>>Foo=type(
...'Foo',
...(),
...{
...'attr':100,
...'attr_val':lambdax:x.attr
...}
...)
>>>x=Foo()
>>>x.attr
100
>>>x.attr_val()
100
>>>>>>classFoo:
...attr=100
...defattr_val(self):
...returnself.attr
...
>>>x=Foo()
>>>x.attr
100
>>>x.attr_val()
100
Example4
OnlyverysimplefunctionscanbedefinedwithlambdainPython.Inthefollowingexample,aslightlymorecomplexfunctionisdefinedexternallythenassignedtoattr_valinthenamespacedictionaryviathenamef:
>>>>>>deff(obj):
...print('attr=',obj.attr)
...
>>>Foo=type(
...'Foo',
...(),
...{
...'attr':100,
...'attr_val':f
...}
...)
>>>x=Foo()
>>>x.attr
100
>>>x.attr_val()
attr=100
>>>>>>deff(obj):
...print('attr=',obj.attr)
...
>>>classFoo:
...attr=100
...attr_val=f
...
>>>x=Foo()
>>>x.attr
100
>>>x.attr_val()
attr=100
RemoveadsCustomMetaclasses
Consideragainthiswell-wornexample:
>>>>>>classFoo:
...pass
...
>>>f=Foo()
TheexpressionFoo()createsanewinstanceofclassFoo.WhentheinterpreterencountersFoo(),thefollowingoccurs:
The__call__()methodofFoo’sparentclassiscalled.SinceFooisastandardnew-styleclass,itsparentclassisthetypemetaclass,sotype’s__call__()methodisinvoked.
That__call__()methodinturninvokesthefollowing:
__new__()
__init__()
IfFoodoesnotdefine__new__()and__init__(),defaultmethodsareinheritedfromFoo’sancestry.ButifFoodoesdefinethesemethods,theyoverridethosefromtheancestry,whichallowsforcustomizedbehaviorwheninstantiatingFoo.
Inthefollowing,acustommethodcallednew()isdefinedandassignedasthe__new__()methodforFoo:
>>>>>>defnew(cls):
...x=object.__new__(cls)
...x.attr=100
...returnx
...
>>>Foo.__new__=new
>>>f=Foo()
>>>f.attr
100
>>>g=Foo()
>>>g.attr
100
ThismodifiestheinstantiationbehaviorofclassFoo:eachtimeaninstanceofFooiscreated,bydefaultitisinitializedwithanattributecalledattr,whichhasavalueof100.(Codelikethiswouldmoreusuallyappearinthe__init__()methodandnottypicallyin__new__().Thisexampleiscontrivedfordemonstrationpurposes.)
Now,ashasalreadybeenreiterated,classesareobjectstoo.SupposeyouwantedtosimilarlycustomizeinstantiationbehaviorwhencreatingaclasslikeFoo.Ifyouweretofollowthepatternabove,you’dagaindefineacustommethodandassignitasthe__new__()methodfortheclassofwhichFooisaninstance.Fooisaninstanceofthetypemetaclass,sothecodelookssomethinglikethis:
>>>#Spoileralert:Thisdoesn'twork!
>>>defnew(cls):
...x=type.__new__(cls)
...x.attr=100
...returnx
...
>>>type.__new__=new
Traceback(mostrecentcalllast):
File"",line1,in
type.__new__=new
TypeError:can'tsetattributesofbuilt-in/extensiontype'type'
Except,asyoucansee,youcan’treassignthe__new__()methodofthetypemetaclass.Pythondoesn’tallowit.
Thisisprobablyjustaswell.typeisthemetaclassfromwhichallnew-styleclassesarederived.Youreallyshouldn’tbemuckingaroundwithitanyway.Butthenwhatrecourseisthere,ifyouwanttocustomizeinstantiationofaclass?
Onepossiblesolutionisacustommetaclass.Essentially,insteadofmuckingaroundwiththetypemetaclass,youcandefineyourownmetaclass,whichderivesfromtype,andthenyoucanmuckaroundwiththatinstead.
Thefirststepistodefineametaclassthatderivesfromtype,asfollows:
>>>>>>classMeta(type):
...def__new__(cls,name,bases,dct):
...x=super().__new__(cls,name,bases,dct)
...x.attr=100
...returnx
...
ThedefinitionheaderclassMeta(type):specifiesthatMetaderivesfromtype.Sincetypeisametaclass,thatmakesMetaametaclassaswell.
Notethatacustom__new__()methodhasbeendefinedforMeta.Itwasn’tpossibletodothattothetypemetaclassdirectly.The__new__()methoddoesthefollowing:
Delegatesviasuper()tothe__new__()methodoftheparentmetaclass(type)toactuallycreateanewclass
Assignsthecustomattributeattrtotheclass,withavalueof100
Returnsthenewlycreatedclass
Nowtheotherhalfofthevoodoo:DefineanewclassFooandspecifythatitsmetaclassisthecustommetaclassMeta,ratherthanthestandardmetaclasstype.Thisisdoneusingthemetaclasskeywordintheclassdefinitionasfollows:
>>>>>>classFoo(metaclass=Meta):
...pass
...
>>>Foo.attr
100
Voila!FoohaspickeduptheattrattributeautomaticallyfromtheMetametaclass.Ofcourse,anyotherclassesyoudefinesimilarlywilldolikewise:
>>>>>>classBar(metaclass=Meta):
...pass
...
>>>classQux(metaclass=Meta):
...pass
...
>>>Bar.attr,Qux.attr
(100,100)
Inthesamewaythataclassfunctionsasatemplateforthecreationofobjects,ametaclassfunctionsasatemplateforthecreationofclasses.Metaclassesaresometimesreferredtoasclassfactories.
Comparethefollowingtwoexamples:
ObjectFactory:
>>>>>>classFoo:
...def__init__(self):
...self.attr=100
...
>>>x=Foo()
>>>x.attr
100
>>>y=Foo()
>>>y.attr
100
>>>z=Foo()
>>>z.attr
100
ClassFactory:
>>>>>>classMeta(type):
...def__init__(
...cls,name,bases,dct
...):
...cls.attr=100
...
>>>classX(metaclass=Meta):
...pass
...
>>>X.attr
100
>>>classY(metaclass=Meta):
...pass
...
>>>Y.attr
100
>>>classZ(metaclass=Meta):
...pass
...
>>>Z.attr
100
RemoveadsIsThisReallyNecessary?
Assimpleastheaboveclassfactoryexampleis,itistheessenceofhowmetaclasseswork.Theyallowcustomizationofclassinstantiation.
Still,thisisalotoffussjusttobestowthecustomattributeattroneachnewlycreatedclass.Doyoureallyneedametaclassjustforthat?
InPython,thereareatleastacoupleotherwaysinwhicheffectivelythesamethingcanbeaccomplished:
SimpleInheritance:
>>>>>>classBase:
...attr=100
...
>>>classX(Base):
...pass
...
>>>classY(Base):
...pass
...
>>>classZ(Base):
...pass
...
>>>X.attr
100
>>>Y.attr
100
>>>Z.attr
100
ClassDecorator:
>>>>>>defdecorator(cls):
...classNewClass(cls):
...attr=100
...returnNewClass
...
>>>@decorator
...classX:
...pass
...
>>>@decorator
...classY:
...pass
...
>>>@decorator
...classZ:
...pass
...
>>>X.attr
100
>>>Y.attr
100
>>>Z.attr
100
Conclusion
AsTimPeterssuggests,metaclassescaneasilyveerintotherealmofbeinga“solutioninsearchofaproblem.”Itisn’ttypicallynecessarytocreatecustommetaclasses.Iftheproblemathandcanbesolvedinasimplerway,itprobablyshouldbe.Still,itisbeneficialtounderstandmetaclassessothatyouunderstandPythonclassesingeneralandcanrecognizewhenametaclassreallyistheappropriatetooltouse.
MarkasCompleted
🐍PythonTricks💌
Getashort&sweetPythonTrickdeliveredtoyourinboxeverycoupleofdays.Nospamever.Unsubscribeanytime.CuratedbytheRealPythonteam.
SendMePythonTricks»
AboutJohnSturtz
JohnisanavidPythonistaandamemberoftheRealPythontutorialteam.
»MoreaboutJohn
EachtutorialatRealPythoniscreatedbyateamofdeveloperssothatitmeetsourhighqualitystandards.Theteammemberswhoworkedonthistutorialare:
Aldren
Dan
Joanna
MasterReal-WorldPythonSkillsWithUnlimitedAccesstoReal Python
Joinusandgetaccesstothousandsoftutorials,hands-onvideocourses,andacommunityofexpert Pythonistas:
LevelUpYourPythonSkills»
MasterReal-WorldPythonSkillsWithUnlimitedAccesstoReal Python
Joinusandgetaccesstothousandsoftutorials,hands-onvideocourses,andacommunityofexpertPythonistas:
LevelUpYourPythonSkills»
WhatDoYouThink?
Ratethisarticle:
Tweet
Share
Share
Email
What’syour#1takeawayorfavoritethingyoulearned?Howareyougoingtoputyournewfoundskillstouse?Leaveacommentbelowandletusknow.
CommentingTips:Themostusefulcommentsarethosewrittenwiththegoaloflearningfromorhelpingoutotherstudents.Gettipsforaskinggoodquestionsandgetanswerstocommonquestionsinoursupportportal.Lookingforareal-timeconversation?VisittheRealPythonCommunityChatorjointhenext“Office Hours”LiveQ&ASession.HappyPythoning!
KeepLearning
RelatedTutorialCategories:
advanced
python
KeepreadingReal Pythonbycreatingafreeaccountorsigning in:
Continue»
Alreadyhaveanaccount?Sign-In
—FREEEmailSeries—
🐍PythonTricks💌
GetPythonTricks»
🔒Nospam.Unsubscribeanytime.
AllTutorialTopics
advanced
api
basics
best-practices
community
databases
data-science
devops
django
docker
flask
front-end
gamedev
gui
intermediate
machine-learning
projects
python
testing
tools
web-dev
web-scraping
TableofContents
Old-Stylevs.New-StyleClasses
Old-StyleClasses
New-StyleClasses
TypeandClass
DefiningaClassDynamically
Example1
Example2
Example3
Example4
CustomMetaclasses
IsThisReallyNecessary?
Conclusion
MarkasCompleted
Tweet
Share
Email
Almostthere!Completethisformandclickthebuttonbelowtogaininstantaccess:
×
JointheRealPythonCommunityNewsletter(MoreThan45,468PythonDevelopersHaveSubscribed)
GetTheNewsletter»
🔒Nospam.Wetakeyourprivacyseriously.