Going back to the original post:
On Sun, Jan 20, 2019 at 6:43 PM James Lu email@example.com wrote:
Backtick expressions work exactly like lambdas, except that they are bound to the instance they are created in every time that class is used to create one.
?!? bound every time that instance is used to create one -- I have no idea what that means, or why it's useful.
As I understand it, Python has expressions and statements -- expressions evaluate to a value that can be referenced in various ways, statements can do other stuff.
A lambda is an expression -- it evaluates to an function object, which can then be bound to a name, stored in a container, passed to another function, etc. Is this proposal somehow different? does a backtick expression evaluate to a function? It seems from other parts of this thread that is does somethign different with namespaces and globals and locals, or ???
To illustrate, this “percent” property is bound to the instance, not to the
class. class Example: percent = property(`self.v*self.v2/100`)
I do not understand this -- "property" is a decorator, or even more generally, a class which, when called, creates a "property" instance, that follows the descriptor protocol:
As it is currently written, property expects a "getter" method as it's first argument.
So the above would be written as:
@property def percent(self): return self.v * self.v2/100
or, without the decoration:
def percent(self): return self.v * self.v2/100 percent = property(percent)
or, with a lambda:
percent = property(lambda self: self.v * self.v2 / 100))
In any case, property expects a callable as it's first argument that will take an instance as its first argument.
So there is no place for "binding to an instance".
As far as I can tell, other than saving the six characters of lambda, the other thing that this does is provide some implicit argument calling -- how does that work exactly?
in the above example, remember that "self" is a convention, so your backtick example could jsut as easily be:
class Example: percent = property(`thing.v * thing.v2 / 100`)
so when the function object is created, how does it know what arguments it takes? In this case, there is only one name used in the expression, so I guess we could assume that that's an argument, but what if it were:
class Example: percent = property(`thing.v * thing.v2 / x`)
Now we have both "self" and "x" as names to be resolved -- and the function will be called with an instance as the first argument -- so is that first argument "thing" or "x", and what namespace is the other one to be looked for?
You do try to explain this here:
Any variable names that exist when the backtick expression is created are
bound to the expression,
what is "the expression"? -- as a rule expressions can't be bound to.
and the reference to the expression is stored within the expression.
it sure sounds like you are using the same work in two ways here...
Names that do not exist when the expression is created must be passed in as parameters.
so if I have a block of code like:
a = 5 bt = `a + b`
Then bt will be a function that takes one positional argument, so this is the equivalent of:
bt = lambda b: a + b
Which means that if I go and add a "b" above that line of code later on, suddenly the meaning of this expression changes? that sure seems fragile to me!
or is it?
bt = lambda b, a=a: a + b
that is, the function gets the VALUE of a at teh time the function is created?
Such names can also be passed in as keyword arguments. Backtick expressions
are created when their scope is created.
no it getting really, really confusing if there are more than a couple names involved:
a = 5 b = 7 bt = `c * d / a + b *e *f / d * f`
so c, d, e, and f are not defined, so the function needs 4 parameters? are they positional? in what order? if keyword, what is the default?
This seems like a LOT of magic -- and for what? just to save typing?
This can be used anywhere a lambda would feel “heavy” or long. Here are a few use cases where using a backtick expression would allow code to be significantly mote readable:
If/else chains that would be switch statements. Creating decorators. Passing in logging hooks. Writing design-by-contract contracts. (See icontract on GitHub for an example of what DBC looks like in Python.) Tests and assertions.
We're really going to need to examples for these -- I can imagine examples where the code would be shorter, but NOT more readable!