We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.
Feedback

Metaprogramming, from decorators to macros

00:00

Formal Metadata

Title
Metaprogramming, from decorators to macros
Title of Series
Part Number
47
Number of Parts
119
Author
License
CC Attribution 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal purpose as long as the work is attributed to the author in the manner specified by the author or licensor.
Identifiers
Publisher
Release Date
Language
Production PlaceBerlin

Content Metadata

Subject Area
Genre
Abstract
Andrea Crotti - Metaprogramming, from decorators to macros Starting off with the meaning of metaprogramming we quickly dive into the different ways Python allows this. First we talk about class and functions decorators, when decorators are not enough anymore we'll explore the wonders of metaclasses. In the last part of the talk we'll talk about macros, first in Lisp and then using the amazing macropy library. ----- This talk is a journey in the wonderful world of metaprogramming. We start off with the meaning of metaprogramming and what it can be used for. Then we look at what can be done in Python, introducing function and class decorators. When decorators are not enough anymore we move to the black magic of metaclasses, showing how we can implemement a simple Django-like model with them. In the bonus track we'll talk about macros, as the ultimate metaprogramming weapon, showing briefly how Lisp macros work and introducing the amazing [macropy library].
Keywords
Macro (computer science)TwitterComputer programmingMeta elementLink (knot theory)TwitterCodeCountingSlide ruleOpen setComputer animationLecture/Conference
Social classMeta elementMacro (computer science)Computer programmingAbstract syntax treeComputer programRun time (program lifecycle phase)MetaprogrammierungSlide ruleComputer programmingMomentumMultiplication signProcess (computing)Parameter (computer programming)MereologyWritingDifferent (Kate Ryan album)Software developerMetaprogrammierungRun time (program lifecycle phase)Lecture/ConferenceComputer animation
Product (business)Line (geometry)Domain nameComputer programmingAbstractionSlide ruleCodeLecture/Conference
Meta elementDrop (liquid)Computer-generated imagerySample (statistics)Price indexGastropod shellObject (grammar)DivisorSoftware engineeringTorusSineHill differential equationConvex hullInternet forumOvalFunctional programmingParameter (computer programming)WordExpressionInterpreter (computing)Process (computing)Form (programming)Video gamePoint (geometry)NumberRecursionFunctional programmingMultiplication signProgramming languageBranch (computer science)Analytic continuationComputer animation
Function (mathematics)Performance appraisalSymbol tableNeuroinformatikElectronic mailing listLimit (category theory)System callVariable (mathematics)Form (programming)SineElement (mathematics)Trigonometric functionsPerformance appraisalProduct (business)Computer programmingCategory of beingMoment (mathematics)ExpressionOperator (mathematics)Set (mathematics)Branch (computer science)Arithmetic meanMultiplicationFunctional programmingMacro (computer science)Representation (politics)Multiplication signPoint (geometry)Sampling (statistics)Parameter (computer programming)Sign (mathematics)Cone penetration testCodierung <Programmierung>Level (video gaming)Programming languageComputer animation
CodeMeta elementDivisorFunction (mathematics)Variable (mathematics)BacktrackingPerformance appraisalRepetitionThread (computing)Personal digital assistantSensitivity analysisFunctional programmingForm (programming)BitDifferent (Kate Ryan album)Image resolutionComputer animation
Function (mathematics)Macro (computer science)Abstract syntax treeDifferent (Kate Ryan album)Equaliser (mathematics)Pattern languageFunctional programmingSocial classInteractive televisionExpressionPosition operatorMultiplication signComputer programmingDirection (geometry)Run time (program lifecycle phase)Macro (computer science)Meta elementAbstract syntax treeComputer animation
Equivalence relationFunction (mathematics)Letterpress printingFile formatDean numberKey (cryptography)Endliche ModelltheorieMeta elementSoftware testingGastropod shellProcess (computing)Physical systemDefault (computer science)Data typeInformationInteractive televisionAsynchronous Transfer ModeSpecial unitary groupModule (mathematics)Directory serviceCodeData bufferSlide ruleData conversionGUI widgetBit rateThermal expansionLambda calculusOrder (biology)Attribute grammarMultiplication signResultantFunctional programmingLetterpress printingData storage deviceHardy spaceMessage passingFunctional programmingType theoryTotal S.A.Prisoner's dilemmaSocial classCondition numberState of matterConnectivity (graph theory)Lecture/ConferenceComputer animation
CodeFunction (mathematics)Letterpress printingFile formatFunktorGastropod shellData buffer10 (number)Hand fanPositional notationMoment (mathematics)Electronic signatureKey (cryptography)Meta elementEndliche ModelltheorieSoftware testingAvatar (2009 film)Execution unitDreizehnoutputSocial classDependent and independent variablesLambda calculusInstance (computer science)Equivalence relationSoftware testingInheritance (object-oriented programming)INTEGRALDifferent (Kate Ryan album)Radical (chemistry)QuicksortFunctional programmingAlgorithmTheorySocial classMathematicsMultiplication signBitDependent and independent variablesElement (mathematics)ResultantTerm (mathematics)Invariant (mathematics)CodeContent (media)LiquidGroup actionEndliche ModelltheorieSource codeRun time (program lifecycle phase)CASE <Informatik>Product (business)Electronic signatureImplementationType theoryComputer programmingModule (mathematics)MetaprogrammierungTupleMeta element2 (number)NP-hardMessage passingXMLComputer animation
Social classInstance (computer science)Equivalence relationFunction (mathematics)Meta elementoutputCodeDynamische GeometrieUsabilitySpecial unitary groupObject (grammar)Data typeParameter (computer programming)TupleEndliche ModelltheorieDefault (computer science)Boilerplate (text)InformationIntegerAttribute grammarEquals signHacker (term)Field (computer science)Data modelSample (statistics)Category of beingType theorySocial classBitFunctional programmingCodeSampling (statistics)ImplementationObject (grammar)Data storage deviceSoftware frameworkRun time (program lifecycle phase)IntegerField (computer science)Military baseCategory of beingObject-relational mappingInformationEndliche ModelltheorieMessage passingSoftware testingDefault (computer science)Data dictionaryElectronic mailing listDifferent (Kate Ryan album)Multiplication signMeta elementCASE <Informatik>Element (mathematics)String (computer science)TypprüfungDatabaseSlide rulePoint (geometry)Inheritance (object-oriented programming)Attribute grammarBoilerplate (text)Parameter (computer programming)Instance (computer science)Volume (thermodynamics)Hazard (2005 film)Compilation albumForm (programming)Basis <Mathematik>Exception handlingMögliche-Welten-SemantikNominal numberDomain nameObservational studyVariable (mathematics)Lecture/ConferenceComputer animation
Process (computing)Key (cryptography)Data bufferEndliche ModelltheorieMeta elementSoftware testingSI-EinheitenField (computer science)Data modelString (computer science)Data typeReliefError messageDefault (computer science)Category of beingNormed vector spaceInversion (music)CNNLatent class modelElectronic data interchangeException handlingUniform resource nameInstance (computer science)Simplex algorithmFunction (mathematics)Parameter (computer programming)Endliche ModelltheorieSocial classInstance (computer science)Object (grammar)Type theoryException handlingAttribute grammarFunctional programmingField (computer science)Electronic mailing listMultiplication signTypprüfungInheritance (object-oriented programming)Loop (music)BitLocal ringString (computer science)Constructor (object-oriented programming)Message passingDifferent (Kate Ryan album)IntegerMeta elementSet (mathematics)Slide ruleExecution unitMathematical analysisTask (computing)MetamodellCondition numberKey (cryptography)Cellular automatonVideo gameBasis <Mathematik>Computer animation
Inheritance (object-oriented programming)Data typeCodeComputer fileMeta elementPatch (Unix)Software testingEndliche ModelltheorieVariable (mathematics)RippingFlagParsingNetwork topologySource codeAsynchronous Transfer ModeCompilation albumString (computer science)Host Identity ProtocolFunction (mathematics)Computer virusLetterpress printingJunction (traffic)Hash functionData bufferSparse matrixPower (physics)Functional programmingStreaming mediaSource codeMacro (computer science)Multiplication signLibrary (computing)Social classObject (grammar)DialectBitImplementationString (computer science)Inheritance (object-oriented programming)Term (mathematics)Order (biology)Module (mathematics)FlagContent (media)Equaliser (mathematics)Error messageVariable (mathematics)Thread (computing)Run time (program lifecycle phase)TheoryGoodness of fitMeta elementVarianceNetwork topologyAbstract syntax treeComputer animation
Point (geometry)String (computer science)Macro (computer science)Core dumpEndliche ModelltheorieCodeImplementationSlide ruleComputer programmingObject-relational mappingSocial classFitness functionMacro (computer science)CodeMetaprogrammierungSlide ruleCASE <Informatik>InformationExecution unitComputer-assisted translationSystem callComputer animation
Programming languageMacro (computer science)Software maintenanceMetaprogrammierungLogic gateSerial portLecture/Conference
Semantics (computer science)Macro (computer science)Function (mathematics)Mechanism designTransformation (genetics)Abstract syntax treeSocial classWebsiteImplementationComputer programmingProgramming languageComputer animationLecture/Conference
Transcript: English(auto-generated)
So, hi, everyone, this is my Twitter account, and this link here that you can see, you can
already download all the material I'm going to show, there will be the code I'm going to show and the slide source, so you can have a look and play around. You're welcome to open Python and interactively try the examples I'm going to show you, because that's what I will do as well. So, I come from Italy, but I live in London, and I work for this company called Dipop,
this is just a small advertisement slide where I tell, just telling you that we are hiring, we're looking for API developers, and if you're interested, just have a look there, dipop.com, jobs, we're using Django, Celery, all nice things, MongoDB, Postgres, et cetera, et cetera.
All right, so what are we going to talk about today? So, first of all, I'm going to try to define what is metaprogramming and why it is useful, and then we are going to talk about Lisp a little bit, but not too much, so don't be scared, and then we're going to talk about Python and how some techniques we can use for metaprogramming
in Python. So, this is the definition I found on Wikipedia about metaprogramming, I don't know if it's the best one, but let's try to go with this one. Metaprogramming is the writing of computer programs that write or manipulate other programs or themselves as their data, or that do part of the work at compile time that would be
otherwise done at runtime. So, this is kind of still not very clear, but I think we're going to see now in some examples how this, what this really means. I manipulate the program itself, manipulate themself, well, I don't know, I'll show
you later what that means. So, why on earth would you do that? That's the most important question, because that sounds really magic and really strange. There are some scenarios where this really makes a big difference, and normally it's
used, it's not really used for performance reasons or for making a better product, it's maybe normally just used to be closer to the domain you're working in, so you write an abstraction in a more powerful way, and in the end you manage to minimize the line of code you have, or you manage to make the whole program more close to your domain,
and more easy to understand for the people in that domain. So what's the cost of that? Well, if you do it well, nothing. If you do it wrong, you might screw yourself up a little bit.
So next slide. Just a very small introduction to Lisp, so it was invented by McCart in 1958, so that was a very long time ago, it's the grandfather of languages in a way, and what people think about it is that there are lots of parentheses, that's how people see it normally.
There is a reason, however, for all these parentheses, and then I'm going to try to show you what it is. This is the simplest possible, well, the simple factorial function, and this is how it's defined. There is a special form defined, which is a way of defining functions, which takes
an argument, which is the name of the function, the arguments of the function, and then there is another special form if, which takes three arguments, the conditional, the then, and the else branch here.
And all these things are expressions, as expressions to be precise. I'm going to do a lot of kind of live coding, so maybe I'll try now for the first time to just show you how things actually work. So we go in Emacs, yeah, of course, and I define, this is the replica for SBCL, which
is a common Lisp interpreter, and then I here I define factorial, I compute it, everything works fine.
Interesting thing to note is that even if I give a very high number, it doesn't blow up like Python would do, even if it's recursive, because it's smarter about recursion than Python. So let's continue. So Lisp has very small, very limited syntax, and there's another property, which is quite
uncommon and not of many languages, which is homoeconicity. That's a very difficult word, not easy to define, but practical means that the program itself is a valid representation of the data.
So here, for example, what we have is the computation of the product between the sine of one and the cosine of 2.03. And what this actually means is that it's like setting an expression to a list where the first element of the list is a symbol star, the second element is another list
with symbol sine and 101, and the third is another list with cosine and 2.03. And then you evaluate that expression. So why is homoeconal that word? This because you can actually transform and pass around a program and manipulate it easily
as it was data. So to make it more, maybe more clear, I try to say, how is Python homoeconal? Well, no, but how would it look like if it could be, for example? That might be something, I don't know if that makes sense, but suppose you can write,
suppose this is valid Python, this is a way to define a function. You create a list, and then the first element of the list is def, then you have function, then you have arg, and then comma pass. So this is a list, and this is also how you define a function. It's not true, of course, but that's what it would look like if it was.
So why this thing is so important? Well, first, this is just to show how the evaluation is done in Lisp, just to be clear. As you know, every operator is infix, and then everything is cons in the end.
So you have here the star, the multiplication sign, then you have the two, then you have another branch where you have the plus, the three, the four, and here nil, a nil, because there is no other element in the cons. So why is this property important? Because in Lisp you can make programming to a very deep level, you can make using macros.
So assume, which you may have ever seen, but in Lisp to set, to do an assignment of a variable, you do set q x 10, for example, which sets to the symbol x the value 10.
Suppose I want to do something like set 10 to two variables at the same time, like assigning two things to one value in one call. Is that possible? Well, at the moment it's not possible, but what if I write a function, so I write this
function define set q f x y z, and then I do this, I use this special form program which concatenate to multiple expressions, but within them. But that doesn't work, so we can try it out, see what happens.
Where was it? Set q. So now I do set q to f, I do a, b, and then I say 10.
It fails, it says the variable a is unbound. So well, how do I solve it? But that's kind of understandable in a way, because I'm trying to set, to create a special form with the function, so that's not really possible.
So, however, if I rewrite this thing with the macro, where the only difference is that here I use def macro, and here I use this back quote, and these commas here in front, instead everything works fine.
See, I define the macro, and then I do set q to a, b, a, b, everything works. So what's the difference? The difference seems small, but it's quite relevant, because here, this macro is basically creating compile time, a new function that will get be templated when you call it.
So this is like, it takes the symbol, it doesn't try to evaluate it, it doesn't try to evaluate a or b, it just passes around to this quoted thing, which then gets evaluated
at runtime, and this runs normally, as it should. So a very powerful technique. Can we do the same in Python? Almost. Well, I mean, to do this kind of thing, you could do something like that, which is horrible, and yeah, almost works.
So basically, you pass two strings, like a and b, and then you pass an expression, you evaluate the expression, and then you update the globals with this thing. It's not even the same, exactly, but it kind of works, but yeah, don't do that.
So yeah, the Lisp prior introduction is over. We can pass something more interesting in Python. So what can we do in Python for metaprogramming? We can have function decorators, class decorators, meta classes, and then if you really want to be fancy, you can manipulate the syntax tree.
I'm going to show them in order. They are also in order of difficulty in a way. What is a decorator first? A decorator is just a syntactic sugar for a function that modifies the behavior of another function or a class.
So this simply means this below. You do an at decorator as the function, and the same thing as defining the function and then redefining, assigning it to the return result of the decorator itself.
And let's see the first problem that we try to solve with decorators. Assume I want to measure the time spent in a function and print it out to the standard output and see how long everything takes. This is a decorator that does it. So it takes the function, it uses this nice wraps decorator, define it in functools,
and this is just to make sure that all the underlying attributes like doc and so on gets passed correctly and they don't get lost along the way. It would work anyway. It's always good to put this.
So yeah, and then you define an inner function which will call the original one, store the result somewhere, store the time before and after, and then print out the time spent and return the original result. And then here you return this inner function.
You can show this how that works. So now we go to the shell, I open it, I type it in. Can you see in the back?
Bigger maybe? Next, okay, okay, okay.
What was it called? Decorators, time, I print, okay. So now I create another function, time it print. There's something, time me, doesn't do anything useful.
Then I call this and I get this message here. Function time me took, they should have said something else. Ah, because I changed it before the talk of course and I made a mistake.
This, sorry, no, still the same.
Ah, yeah, yeah, yeah, thank you.
All right, I can reload the decorators, so this works, no. What? No, just R.
Okay, let's do, I showed you the next example, it's more interesting. All right. Okay, so we have another problem.
I'm still with timing but slightly different because I want to know when I'm implementing something new, like a new feature or I change the algorithm, I want to know that the new one runs faster than the previous one. But I just, I don't want just to know it once, I want to make sure this always like that, so I want to have that running in a test somewhere, like integration test
and say my new fancy algorithm is still faster than the crappy old one after six months of hacking on it and adding things. So well, I mean in theory with the previous approach we couldn't do that because we were
just printing it out so we can't just check all the time how things look like. But with another decorator which also changed the signature of the original function, then we can do that. So what I have here is a test to check that the first implementation is faster than
the second one and the way it works is that the new, this decorator here will change the function return values and add the time spent in the function as a second return value. And so I do this, I do this for the second one and then I can check that the time of
the first one is less than the time of the second one. So now let's see how we actually implemented them. So still quite simple, actually exactly the same almost. The only difference is that here when we do the return we also add the time here.
So this is an interesting case and the difference is that you can't use it in an import time way because if you decorate your function in the module like that then it will mess
up everything because you don't care about the time spent in production. So you can only do that in this way but it's perfectly valid thing as I was saying since it's just in TatiSugar to just replace the function at runtime in this way and then call it. So let's see if this, I have more luck with this one, so decorators, oh yes that works.
So yeah, see I get a tuple and the first element is the result and the second element is the time spent, very nice. To actually see how things work, it's interesting also to look at the code here of time me
and yes and the wraps function did a nice thing to also make sure that the source code that is returned is the original one not the wrapped in the decorator.
Alright, so another example, this is how you can decorate a class instead, suppose I want to add always the same method to many classes and this, I mean, I don't want to go in every
class and change it and I don't want to make a super class to make this, this is another very simple way to do that, you just, this is the test where I have a class which adds a response method and then when I associate this class and I call response
I get the return I want, the value I want. So this is the simple way you do it, very clear and simple. You just take the class here, you assign to response an anonymous function which takes self and returns this and then you return the class itself.
Yeah, that's all for decorators for now. So let's pass to meta classes. Meta classes is the next way of doing meta programming in Python, it's more powerful and it's more, it's also a bit more difficult to understand in the beginning but it's really not that hard, as hard as it looks as I'm going to show you.
So what's a meta class? Well, a meta class in Python is just the same as a meta class tends to a class like a class tends to an instance, so it's a type of a class.
If you create a class in this way here, it's exactly the same thing as doing it in this way with the type function. I'm going to try to show you now a little bit. For example, if I now do this, I forgot to mention in the beginning that all the code
I'm showing is Python 3 only, well the difference is not many but yes, if you try to run it on Python 2 it might fail or it might not do what you want. Okay, so what's the type of C? Type.
And then if I try instead to create a type, let's see what this takes as an argument, it takes an object or name, a list of bases and the dictionary. So for example, suppose I want to create a runtime, a class like B, which has bases
on the object and then as dictionary I have a class attribute A10, must be a tuple, yes mistake, okay, so this is now a class and I can instantiate the class and I can access
the attribute A. So this is another way of creating classes at runtime and this is how in the background it works. Let's see the simplest possible meta class implementation and how to use it.
So we have a class simple meta which subclasses type and then to use it in our own class we only need to create class subclassing object and then passing meta class equal to simple
here. In Python 2 it would have been underscore underscore meta class here equal to something, but it's just the same thing. And now let's pass through some more interesting examples I think.
In every ORM or every Django framework or something, it's very easy to define models that talk to database, store data, keep some information about the type and so on and this is kind of possible syntax that is used which I just made up but it's quite similar
enough I think and it looks like magic but it's not that hard to do in reality and the simple way to do it in this case if I want to accomplish something like that, if I don't have meta classes, I could just have a model for this particular case
which takes the argument x and y and then initialize them like that. So well I mean you can do that if you have some very simple scenarios but if you want to define many models, if you want it's very verbose because every time you have
to declare everything and assign it there is a lot of boilerplate and also the very important thing I think is that you have no information about the types and while Python is duck type, databases are normally not so you still need to have the information somewhere.
So how can we accomplish this kind of very nice thing in Python then? We start by looking at how we can implement a field type so which is this one. So a field type will be a base class
for things like integer or string and you can implement it so this is the test that will this the requirement for this kind of type. I want to have three different things I want to be able to wait a minute I'm not this there's not the right test okay this one
is like this sorry sorry no take it back this is the next slide. So this is the the model I want to have and these are the three requirements that I have for this model to behave like I want. So I want to be able to create an object like this passing for example y
and without passing x, x has default value y doesn't have one so I checked that after I pass this y is set and x is also set to the default value. I want to be able to know that
if I pass something wrong like unknown hello it will give an exception and if I pass something of the wrong type it will always give a type error in this case. So first we can look at how fields are implemented and here again there's a requirement for the fields so if I create an
integer where I pass zero as value and the default one the value will take the precedence if I try to set at the init time something which is the wrong type I get the type error and also very importantly if I in the beginning create the element correctly and then I change
the type to something else and then I also get a type error. Let's say an implementation of fields first which is not you done using metaclasses but it's done in this way so they need the most important thing is that we have a property here which has a also a setter
and this property is the actual representing the value and whenever you try to set here the value which is also done at this point it will always check the type it checks if the type is not
an instance of this base type whichever class will have to define then I raise a type error let's see how is for example the implementation of a string or integer so very simple here we have
the base class here is integer and here is string so that's all all I have to do is to define the base type and if the object or passing is not an instance of that then it will give type error. So let's go back now to the model the model's metaclass and this is the actual
instead the solution for all this which fits in the slide so it must not be that difficult is the solution for the original problem. The way it's done is that a subclass type
as I've done before in the previous example and then I override the __new method which is used to in during the construction of the class itself instead of the initialization initialization of the object like init
and then I get four arguments here I get the class I get the name I get the list of base classes and I have this important the class dict which is all the the attributes and methods of the class basically which we can modify.
So the way I want to do this is basically just replacing the init of the new class that I will create now with another init that will do some extra checks and extra
things for me automatically every time. First of all I loop over the attributes of the class and I check if the if the attribute of the class is an instance of a field then this is a field I just stored it in a dictionary here and then I redefine an init here which
takes some KWX so it can take X equal one Y equal two and so on and so forth and then it will first of all check if there are some arguments which I pass in that are not known so if the set of the difference between the K the arguments passed and the fields that
found looking inspecting the class is greater than zero then give an exception otherwise if not if everything is fine then loop over this and then generate
set an attribute to this object self with the as a key the name of the the argument and there's a value the value which I just now created by calling the class and passing
the value so sounds more complicated than this maybe but I try to show maybe now how it works and then after that the important step is that I redefine the init of the class take to the thing which I already defined here and then I return just the super call and try to show you
just very simple how this behaves maybe so if I now yes now the only thing I need to do
is to define a model in this way class model yes and then I will simply if I define now another model um oh that's sorry type today but for model and then I define x equal to models
string okay now suppose I want to create an instance of this but I pass the wrong thing complaints and no arguments y if I pass this it checks the type and there's another
if I pass this instead everything will work correctly oh sorry and the interesting thing if if I look at the init of this method it tells me where actually it comes oh no sorry
if I look at this if I look at the init of the class simple too it tells me that it comes from here function models meta model new locals init so that's where it finds it all right
another another example which is a bit more simple is how to for example make sure that we always call the super of the init in the subclasses of a super class that we define um I've seen this doing for example in the module threading.py if you forget to call the init
it will blow up and complain and if you look at the implementation it's basically just this it has initialized false and in init it will set initialize true and in every method that has to be working on an initialized object there is this check so that's a bit
annoying and that's a bit too long maybe to do so this is what I want is that I call something and if I forgot to call the super in the init it should give me an assertion error and that's it that's the implementation very simple I do I store the original init somewhere
and then I call it here I replace the init with another init which I'm doing I call it here just to make sure I don't lose anything and then I also add an assertion looking for a flag variable
that is set so this is kind of doing the same thing but it's in a more generic way and so all you have to do in your class to use this is to just go um here I define a class base passed
in the meta class checking it and all I have to do is to define a flag variable equal var and this is a variable that I know that I always set in the init here so if something tries to subclass base and forget to call init this here will be not set
and then this will give an assertion error yep and last thing I want to show very quickly is that just you can do even much worse than that if you want and this is a very simple function that analyze the aest of a given function
parsing it parsing the source and then trying to find out a particular value a string and replacing it with something else to show you the actual example is probably easier to
understand this is um this is the function here which I want to replace a runtime and what I want to replace is to remove the hello from the string so and this is what it's doing I call it I exec this um the return of this and then I call it again so what is going to happen
is that I don't know yeah the first time it calls with hello world the second time only one
all right everything works fine um so the thing is that of course this is very hard-coded and it's not really uh the good idea but in theory ast is just a tree so you can traverse it in any way you want so you can look for all the things that look like strings
adapt this content or you can look for all the you can do anything you want and you can put that in a decorator or you can and in this this is the only way I think that you can reach something like as powerful as lisp does in python and there is a library which I'm going to
I don't have time to show because I knew it was too much there is a library called macro pi where you can actually do things like that and this is valid python even my syntax highlighter doesn't think it is but it will run and it will work and you can make a case class where you declare a class like that passing x and y and this automatically will
create a init with x and y and you can just instantiate it like that and everything will work correctly and that's how it's implemented so if you want to look it up it's called macro pi so yeah that's it for me so any questions and then just the conclusion sorry uh well
meta programming is fun you can make it in just one fit in one slide the code to create a simple style orm and then just be careful but uh yeah use it if you want
yeah would you still be using metaprogramming and yeah sorry if I knew that the maintainer
might be a psychopath and serial killer would I still be using meta programming uh yes but I would comment it maybe leave out my name or uh or change the history of gate to make sure it doesn't come to me yeah
another question
I think the philosophy of python is that you shouldn't do things that are too unreadable and too hard to understand so it would kind of go against the the language design to add more and I don't know what really you could but there are things you can do crazy things like
macro pi does so it is possible already it's just I think it's enough and I wouldn't think you need more than that yeah thank you