#internals-and-peps
1 messages Β· Page 107 of 1
Basically, imagine in python you had a free function:
def add(x: Sequence[T], y: Sequence[T]) -> Sequence[T]:
...
hmm.... if list actually inherited from collections.abc.MutableSequence then you could get closer
Then you can see that it's reasonable that z = add(x, y) will work
because x and y are both of type Sequence[Foo]
x is a Sequence[Bar1] and by covariance its Sequence[Foo]. same for y.
The problem in python is that member functions are just very specifically bound to that type, they can't make any broader assumptions.
Basically, imagine in python if there was actually a way to define operator+ on Sequence, instead of on list
that's what's happening in Kotlin
there's no way to do this in python and that's why this stuff doesn't work out nicely
and coincidentally it's the exact same reason why we can't just have operator+ on iterators, and Kotlin can
yeah makes sense
anyone know if there's a way to get an error message out of the discords api "tasks.loop" loop? when it errors it just stops with no message or anything.
i have copy pasted this question into a help channel
Consider a class like the Thing in the example below, and assume there are Good Reasons for doing it this way.
In do_something, the _do_* methods are guarded by the if self.db is None check, in that they can't be executed if the db is None. However, there's no way to signal to the type checker that this should always be true; I either have to add the same if check to each of the _do_* methods, or do a cast which could result in a runtime error if one of these methods happens to be used incorrectly, and would not be caught by the type checker.
Is this even possible without dependent types? How do other languages with nullable-but-null-safe types handles this case?
import sqlite3
class Thing:
db: Optional[sqlite3.Connection]
def __init__(self, Optional[sqlite3.Connection]):
self.db = db
def _do_insert(self, obj_id):
self.db.execute(
'insert into mystuff values (?)',
(obj_id,)
)
def _do_delete(self, obj_id):
self.db.execute(
'delete from mystuff where id = ?',
(obj_id,)
)
def do_something(action: str, obj_id: str):
if self.db is None:
raise RuntimeError('Cannot do things without a db.')
if action == 'insert':
self._do_insert(obj_id)
elif action == 'delete':
self._do_delete(obj_id)
else:
raise ValueError(f'Unknown action: {action}.')
Ideally I'd be able to annotate _do_insert and _do_delete with something stating that "this should never be called if self.db cannot be proven as non-None"
https://kotlinlang.org/docs/null-safety.html it looks like the Kotlin ?. operator lets you write self.db?.execute which is null-safe in that it will be a no-op (return null without taking any action), and the compiler will complain if you don't, but you can't tell the compiler to "never let me call self._do_delete if self.db is None".
Hey @fresh willow, please don't post random memes on the server
Neither you @stoic locust
@paper echo i'm not sure I fully understand what it is that you want
Oh, maybe I better understand
So, in Kotlin for example, you would actually define these as extension functions on self.db
in which case, as you say, self.db._do_insert is a compilation error
self.db?._do_insert will be a no-op if it's null, but that's what ?. is, it's an explicit request to do exactly that
not letting people calling methods with ?. because of nullability makes no sense
Really though the basic idea of this solution applies here as well. You just need to make _do_insert and _do_delete static methods instead, that take an sqlite3.Connection
instead of member functions taking self
there's no special annotation. By taking self, you've already accepted that you're taking an Optional[Connection]. So you have to stop taking self, and instead take a Connection
the best solution is generally raii, where the object acquires what it needs in __init__. It doesn't work as well with async, but it is still viable
this seems pretty orthogonal to the problem here, tbh, even supposing that python has RAII, which in itself is a bit of a stretch
We don't know why the database is Optional here, it could be for reasons unrelated to resource management
well, an object that needs a database ideally shouldn't have it nullable. I am not aware of a language that solves this nicely. The better solution would just be taking as an argument, yeah.
There's also trade-offs between Thing opening a database connection itself, and dependency injection, where Thing just receives some object that provides a suitable API, which one makes sense requires more context than has been given (and again, isn't really part of the main problem here)
Well, then you're basically saying that the argument shouldn't be nullable, but salt asked us to accept that there are good reasons for that
I guess we could probe into why exactly its nullable
this is an interesting solution, and it works in python too. but it does force you to separate the db logic from other logic on self which in my case would make a big mess out of the control flow
you can make indefensible claims to the type checker using assert
You don't really need to separate it, you can take multiple arguments in your static functions
yeah, like self i suppose π
Well, with self you are committing to something specific though, right
in fairness, you could even still take self, but also take a Connection
in this case it's nullable because the database connection is not always available. i am not actually raising an exception in the real application, i am signaling in-band that the database is missing (HTTP 503)
this is slightly weird because now you have two arguments that alias each other, so its less ideal
But, it would still achieve the goal, static type checking wise
users would have to come up with something with type Connection to call the member function
:incoming_envelope: :ok_hand: applied mute to @unkempt rock until 2021-05-11 20:18 (9 minutes and 59 seconds) (reason: duplicates rule: sent 4 duplicated messages in 10s).
:incoming_envelope: :ok_hand: applied mute to @haughty thistle until 2021-05-11 20:18 (9 minutes and 59 seconds) (reason: duplicates rule: sent 4 duplicated messages in 10s).
kids spamming, nothing to see here
def _do_delete(self, obj_id, db: Connection):
db.execute(
'delete from mystuff where id = ?',
(obj_id,)
)
assuming that in the real code you have other uses of self
Again, I'd rather make it static and take all arguments explicitly; if you have so many arguments then it's probably a sign your class/functions are too big
but it's a reasonable quick solution if you don't want to refactor as much
The other option is to make it not Nullable. If the database is not available, then you provide a NullConnection which either does nothing or throws in its API, whichever makes sense.
Which of these two makes more sense, again just depends on a lot of things.
import sqlite3
def _do_insert(thing: Thing, db: sqlite3.Connection, obj_id: str) -> None:
db.execute(
'insert into mystuff values (?)',
(obj_id,)
)
def _do_delete(thing: Thing, db: sqlite3.Connection, obj_id: str) -> None:
db.execute(
'delete from mystuff where id = ?',
(obj_id,)
)
class Thing:
db: Optional[sqlite3.Connection]
def __init__(self, Optional[sqlite3.Connection]):
self.db = db
def do_something(action: str, obj_id: str):
if self.db is None:
raise RuntimeError('Cannot do things without a db.')
db = self.db
if action == 'insert':
handler = _do_insert
elif action == 'delete':
handler = _do_delete
else:
raise ValueError(f'Unknown action: {action}.')
handler(self, db, obj_id)
something like this, then, for the "static" approach?
if you're taking Thing there's literally no point making it "static"
the point of make it a static or free function is to not take Thing
I assume that Thing has more fields, right, not just db
well, if the db connection is never None in that function, you can do
def _do_delete(self, obj_id):
assert self.db is not None
self.db.execute(
'delete from mystuff where id = ?',
(obj_id,)
)
``` and the static checker will believe you
at least mypy uses that
yeah, specifically i would be calling other Thing instance methods before and after the db.execute
yeah this is a good idea; if you hit an assertion error at runtime you know you blew it
Well, it doesn't actually solve your original problem though...
you want mypy to try to prevent you from calling _do_delete in the first place
yep. it's still "duplicating logic" in a way that i wish i didn't have to do
unless you prove that self.db is not None
I think rust can actually do this using some odd features. But that is about the only language with some idea of object states I know of.
in the not-really-static version i showed above, you're basically using the type signatures to do the assertion, and which lets you re-use the proof that was constructed in do_something
class Thing:
db: Optional[sqlite3.Connection]
x: int
def __init__(self, db: Optional[sqlite3.Connection], x: int):
self.db = db
self.x = x
@staticmethod
def _do_insert(x: int, db: sqlite3.Connection, obj_id: str):
# Type checkign works fine now because db is not optional
...
@staticmethod
def _do_delete(x: int, db: sqlite3.Connection, obj_id: str):
# Type checkign works fine now because db is not optional
...
def do_something(action: str, obj_id: str):
if self.db is None:
raise RuntimeError('Cannot do things without a db.')
db = self.db
if action == 'insert':
handler = self._do_insert
elif action == 'delete':
handler = self._do_delete
else:
raise ValueError(f'Unknown action: {action}.')
# This type checks too, because you would have thrown earlier if self.db was None
handler(self.x, db, obj_id)
yes, type signatures are generally how you want to propagate information about Optional π
that's the whole point right
(you an add py or python after the ``` to get syntax highlighting)
In the example I posted, if you didn't have the check and raise, then mypy would complain
because the type of handler would be a callable that is not compatible with the call that you made
Well, yes, except your argument takes thing, at which point, like I said, you may as well make it a member function π
your functions, sorry
right. in either case, you can still mistakenly access thing.db, instead of using the db parameter, but at least the type checker will identify the former case correctly as maybe calling None.execute
It's less desirable to pass Thing because when you call a function you don't really want to pass the same argument twice, even if indirectly
No, in my case there's no thing argument
so you can't
i meant to delete the "in either case"
yes, you're right
Yeah. If you are already requiring a non-nullable db, then passing a nullable one as well is less optimal, which is why it's better to not pass Thing
that makes sense. in my particular case i'd have to further refactor code around it so i'm willing to accept the localized weirdness
but obviously if you have tons and tons and tons of properties in Thing, and you need to use many of them in _do_delete, you may still want to pass it
Yes. Although, again, if you are doing that, just leave them as member functions
def _do_delete(self, db: sqlite3.Connection, obj_id: str):
# Type checkign works fine now because db is not optional
...
No point making a function static just to pass self anyway
is there any way to set a default value (eg. argument = "nice") and required type (eg. argument: str) to an argument
the NullConnection thing is interesting too, could be useful in other cases (or i could try/except around the errors that it raises internally - but that's suspiciously like Using Exceptions For Control Flow)
@placid kraken argument: str = "nice"
thanks
yeah, I mean the NullConnection would be useful depending on what your plan is
what does Thing actually plan to do if db is None and some method that requires not-null db is actually called
if it's always going to throw, then you may as well just have a NullConnection that always throws and get rid of the Optional
it's the exact same thing with less boilerplate in Thing, which is a win IMHO
If you're going to try to do nothing without failing, e.g. always do nothing, returning empty collections, etc, whatever, then that can also be handled by a NullConnection idea (just a different implementation)
and again you remove boilerplate and improve modularity/encapsulation
The less Thing knows the better. So in the end you have to evaluate if ther eis really a reason that Thing needs to know about having an Optional[Connection]. Or can everything simply be handled by having the correct Connection implementation.
I like the NullConnection strategy for a client library, e.g. the Python GraphQL library gql which lets you inject a "transport" that determines how to send the requests (async or sync). Something like a NullTransport in this case would make sense to me.
however if the public Transport interface is sufficiently complicated, the implementation of NullTransport could end up really verbose or really ugly (eg. using __getattr__)
i will move the rest of my question to #software-architecture , i think this is getting away from "advanced" typing stuff and more into mundane territory
love the pfp quick π
pfp?
@neon mesa
oh, profile picture. Thanks π I need to trim it, there's some funny whitespace at the top and bottom
Anyone have anything interesting to share?
>>> tup = ([1],)
>>> tup[0] += [5]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> tup
([1, 5],) # Errors, but 5 still gets appended
>>> tup[0] = tup[0] + [6]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> tup
([1, 5],)
>>> tup[0].extend([6])
>>> tup
([1, 5, 6],)
the second one or the third one don't really surprise me
But for the first one, I discovered that x += y isn't just x = x.__iadd__(y), it's more like:
temp = x.__iadd__(y)
x = temp
Why is it this way though?
!e ```python
tup = ([1],)
try:
tup[0] += [5]
except Exception as e:
print(e)
finally:
print(tup)
@paper echo :white_check_mark: Your eval job has completed with return code 0.
001 | 'tuple' object does not support item assignment
002 | ([1, 5],)
I've seen this a while ago. This is not a bug
If you follow it along
temp = x.__iadd__(y)
x = temp
then it makes sense that the list gets modified but then the tup[0] = temp part fails
that makes sense
naively i would have expected += to be applied to "the thing referred to by tup[0]"
so yeah this is on par with the "unbound local" gotcha
!e ```python
x = 3
def f():
x = x + 10
return x
f()
@paper echo :x: Your eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 5, in <module>
003 | File "<string>", line 3, in f
004 | UnboundLocalError: local variable 'x' referenced before assignment
that's always a fun one to explain in a help channel
i don't actually know the reason for that
The semantics of EXPR += y are:
x = EXPR
new = EXPR.__iadd__(y)
EXPR = new
so yeah, this makes sense.
Although I totally agree that x += y being distinct from x = x + y (and especially being .extend on lists) is cursed
i think the idea is that because x is on the left hand side, cpython creates a binding for x local to f, but without anything bound to it yet, so referring to it in the rhs refers to the unbound local binding x
!e ```python
import dis
x = 3
def f():
x = x + 10
return x
dis.dis(f)
@paper echo :white_check_mark: Your eval job has completed with return code 0.
001 | 4 0 LOAD_FAST 0 (x)
002 | 2 LOAD_CONST 1 (10)
003 | 4 BINARY_ADD
004 | 6 STORE_FAST 0 (x)
005 |
006 | 5 8 LOAD_FAST 0 (x)
007 | 10 RETURN_VALUE
Yes, an assignment will never work on a global unless you say global x
not very enlightening but i guess it's just a name collision
well the idea would be to assign global:x + 10 -> local:x

why doesn't that work?
a single name cannot be both global and local in the same scope
implementation detail is leaking all over my desk
ahem, clean up perhaps
OSBRN, I'm translating the official Python tutorial now, and it's somewhat shocking how technical and fast-paced it is.
If I started with it as my first Python resource, I would be totally blown away by all the jargon
i think i remember a similar experience
a lot of python docs are like that, clearly written by people who eat sleep and breathe python and whose version of "obvious" is not shared by the rest of us
"on some barely related notion"?
This is the tutorial on inheritance.
https://docs.python.org/3/tutorial/classes.html#inheritance
has a 2003 flavor
It... it doesn't even explain what inheritance is, does it?
sounds like a "python for java users" tutorial
(For C++ programmers: all methods in Python are effectively virtual.)
well, yeah
I suppose this part was written before Java existed lol
actually this looks really useful for the people who wander into python world from c++, java, etc. and wonder why scoping in classes is so weird
I mean, this is actually a possiblity, right?
https://docs.python.org/release/1.4/tut/node62.html#SECTION001050000000000000000
The wording didn't change since 1996
which is, well, one year older than Java
wait actually?
yeah
well, there have been some minor changes, like e.g. -> for example
but no more
so while it's technically older than Java, C++ was probably way more popular and familiar
well, that's one hell of a journey to the past
I dunno, I think it's reasonable for language docs to just explain syntax for relatively basic and common things
Most language docs I have seen don't explain what an if statement does in detail, or a loop
They just say "here's the syntax" and if you don't already know what these things are, yes it'll be confusing
But if you don't know what inheritance is the official python docs probably shouldn't be your first stop
Hello
Does anyone here can help me with my python task ? Simply pm me . It's much appreciated if you can lend me a hand ^^
hey, check out #βο½how-to-get-help . this isn't a help channel
Note the this is the tutorial, not the reference documentation.
yeah, it just seems like the tutorial is aimed at people who're already programmers.
specifically C/C++ programmers π
I think it pretty much needs to be. It's "how to program in Python", not "how to program (in Python!)"
It would need to be much larger to cover everything beginners need to know at the rate they can digest it
That's not to say it can't be spruced up, or that it shouldn't introduce concepts before talking about how they're applied, just that it also shouldn't spoon-feed like a tutorial aimed at teaching beginners their first language should.
yes, it probably shouldn't be a programming tutorial in general
but some parts are a little bit too technical to understand for someone who doesn't already know what's being explained
is there any way to mangle a name differently than with the word after the class keyword?
Might be possible by overriding getattr and setattr dunders
upon investigating a bit, I think the actual attribute name is set by interpreter with the word after class keyword
but yeah some trickery could change how they are found
I only ask cuz my current instructor makes us use mangling for (sic) "private" attributes which are used by subclasses and it leads to all sorts of ugly code to allow for inheritance
so I kinda wanna do something rly cheeky but it seems like the interpreter sets the mangling based on which class block the attribute is defined in so I have to limit my cheekiness
It's done at compile time indeed.
In [9]: class C1:
...: def __init__(self):
...: self.x = 1
...: self.y = 2
...:
In [10]: class C2:
...: x = 1
...: y = 2
...:
In [11]: c1 = C1()
In [12]: c2 = C2()
In [13]: c1.x
Out[13]: 1
In [14]: c2.x
Out[14]: 1
is there ever a case where C1 would be a preferable approach to that of C2 ? It seems redundant if there isn't an input for x,y
though the example here is just ints, mutable defaults is the first drawback that comes to mind for C2
In [1]: class A:
...: a = []
...: def test(self):
...: self.a.append(5)
...:
In [2]: ob1 = A()
In [3]: ob2 = A()
In [4]: ob1.a, ob2.a
Out[4]: ([], [])
In [5]: ob1.test()
In [6]: ob1.a, ob2.a
Out[6]: ([5], [5])
hrm π€
fair - so you'd want to have an init if it's relating to a specific instance of a class rather than across all classes of that type, make sense
In C1 the attrs live on the instance, in C2 on the class, so whichever makes more sense would be preferable
in other languages they would be called "static" variables
I'm sometimes not too sure what to do when i have "class constants"< like if you have:
class C3:
def __init__(self, y):
self.a = 1
self.y = y + self.a
is that the "right" approach?
here a isn't expected to change, and would want to be the same across all instances created of class C3
Using the definition of C2 from your snippet, we do get this behaviour:
>>> a, b = C2(), C2()
>>> a.x += 1
>>> a.x
2
>>> b.x
1
What happens here (please fact check me) is that x lives on the class, but a.x += 1 reassigns on the instance, so now C1 has an instance attribute called x that gets found first in the resolution order, whereas C2 doesn't have the attribute and looks into the class namespace as a fallback
It would be a different case if x was mutable and we didn't reassign
In which case the change would also show up on b, because both a.x and b.x would still refer to the same object
x is referring to the class attribute (?) rather than the type (as int is immutable)
Here's a better demonstration of what I mean:
>>> a, b = C2(), C2()
>>> a.x is b.x
True
>>> a.x += 1
>>> a.x is b.x
False
b.x still resolves to the attribute on the class, whereas a.x now resolves to the attribute on the instance
because the instance namespace is checked first
i need to run through this a bit more - what're your thoughts about C3 though? re the constant thing
that's something I've been uncertain of before - I've felt (for some reason) like I should put them out of the init lol
Constants being behind the class is fairly common, but there are considerations to both class and instance variables if you also need to reassign them as they will behave differently
One more even better example (I think):
>>> a = C2()
>>> a.x += 1
>>> a.x
2
>>> type(a).x
1
So I'd consider this dangerous if your intention is to have "default" attributes, we only get away with it here because of the immutability (so re-assignment is necessary)
what does "behind the class" mean
Living in the class object instead of the instances
so like
class C:
# in class ob
a = 1
def __init__(self, x):
#Β in instance
self.x = x
but that's different @peak spoke , as in my example i wanted to use a's value within the __init__
Now a will live on the class (so "shared" by all instances, if they access it), whereas x will live on each instance
I'm not sure whether it's the right approach because I'm not sure what you're trying to do π
yeah - but you can't have self.x = x + a in that class above - that's what i was trying to do before
You would do self.x = x + self.a or C.a
you would need C.a
as it seemed natural to have all values which weren't passed into the __init__ to be outside of it - not sure why, so that's what that question is about - presumably I'd just have:
self.a = 1
self.x = x + self.a
instead - though if it's typed out of order it'd be weird.
@feral cedar this the recommended approach to this then?
idk what you mean by "out of order", but you are creating it each time you make an instance
ignore that then - i can see C.a works, and am wondering which is preferred between that and using self.a within the __init__. If a is to be held by all instances then perhaps C.a is better?
yes in my opinion. i have no idea if that's actually preferred though
In the example a is not held by all instance though, only by the class
By the way, there's also the option of putting the constant in the module namespace
If it's internal-use only, I think that's often the best option
The difference between self.a and C.a is that if an instance happens to have an attribute named a, it will access that instead
as in
A = 1
class C:
def __init__(self, x):
self.x = x + A
For C.a, you will always access the class attribute
Yeah, I'd do that
Unless there's a reason for A to be accessible on the class
i appreciate this is vague - basically if context is useful - i have some path which is like data/project_1 and there are multiple directories within there - so the metadata for project_1 can have attribute data_root = pathlib.Path('./data/project_1')
It depends on the actual use but I usually prefer for constants only used in the class to also be behind its namespace to keep its scope clear. The usual exception for me is if it's something that may want to get edited as some sort of configuration
Yeah it prob depends on the context too much.. if I had a module with 1 class and 1 path constant used by the class, I'd probably put the constant in the module next to the class
if I had a module with 2 classes each using its own path constant, it would prob make sense for the constants to be on the classes
fwiw I think I always put constant paths in module namespace, unless they are built dynamically at runtime (rather than import time)
I guess import time is run time but you get what I mean π
there are multiple modules and I'm trying to consolidate all the metadata (like paths / output locations etc) into a single place (am also trying to use dvc pipelines more but thats off topic π )
i can make a more concrete example for you next time i have chance, i might have been too vague above rather than abstract
Yea, I think at some point it becomes just personal preference.. someone else may have said that the path should live in some config class elsewhere, if it feels more like config that a constant, etc ..
If you're just trying to decide between module/class then pick whichever makes more sense, and if you can't decide just do one and change if it doesn't work π
Lol, yeah, difficult when there are constant deliveries π
is it possible to pass only two outputs from 3 from an iterable through to another function?
I have an iterable which yields 2 indexes and a label as a result, but I should only pass the indexes to the fuction afterward
itertools.islice is probably what you want
sort-of works. If someone with a bunch of iterator experience wants to take a look at #help-orange it'd be greatly appreciated.
hello
@lofty rover you can write a generator comprehension that only passes the indices
Maybe i"m not understanding exactly. but say you have:
x = [(label1, index1a, index1b), (label2, index2a, index2b), ....]
Or rather, that but as an iterator/generator, not as an actual list
then
y = ((t[1], t[2]) for t in x)
And now y is a generator that only holds the indices
You could just use args in the second second function
def functionOne( ):
return 1, 2, 3
def functionTwo( a, b, *args ):
...
functionTwo( *functionOne() )
So here I am, ticking away on this lexer (it's coming along nicely) and I can't help but think to myself- there has to be a better way
I'm basing mine off of this: https://github.com/m-labs/pythonparser/blob/master/pythonparser/lexer.py
It's not too dissimilar from the built in tokenize.py module, and fairly straightforward
What it is not is elegant, or readable
I've heard of lexer generators in the past and even that they are heavily optimized. Obviously this is a fantastic exercise. Going forward, I absolutely need to have a working understanding of how lexing works, but, is there a sexier solution?
is pythonparser PEG?
oh, just the lexer
i dunno, most lexers i've seen in the wild have been regex based --- i suppose you could build your own finite state machine if you wanted
>>> class A:
... pass
...
>>> class B(A):
... pass
...
>>> class C(A, B):
... def __init__(self):
... print(type(super()))
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Cannot create a consistent method resolution
order (MRO) for bases A, B
wut
C is trying to inherit from A in two different places
i thought this was allowed in python tho
just inherit from B
I'm also curious though, why does this screw with the mro?
Inherits A, then Inherits B, that inherits A again
I mean, the whole point of an mro is to linearize situations like this
So is there some sort of explicit ambiguity? I mean, there must be
you can do it the other way:
In [15]: class A:
...: ...
...:
In [16]: class B(A):
...: ...
...:
In [17]: class C(B, A):
...: ...
...:
so you can inherit from the same class multiple times only if it's diamond inheritance?
That seems a fair assumption
it seems like it doesn't happen for every diamond though
In [1]: class A:
...: pass
...:
In [2]: class B(A):
...: pass
...:
In [3]: class C(A):
...: pass
...:
In [4]: class D(B, C):
...: def __init__(self):
...: print(type(super()))
...:
In [5]: D()
<class 'super'>
Out[5]: <__main__.D at 0x7f3738980070>
unless I made some silly mistake
it's not really a diamond though, more like a triangle, no?
A -- B
\ /
C
maybe the problem is that every class needs to be a fixed number of levels from the derived class
in an actual diamond, the grandparent is 2 away from the child, both ways
in this example, A is both parent and grandparent
So an SO suggests that MRO is good because it has the following properties
- each ancestor class appears exactly once
- a class always appears before its ancestor ("monotonicity")
- direct parents of the same class should appear in the same order as they are listed in class definition ("consistent local precedence order")
- if children of class A always appear before children of class B, then A should appear before B ("consistent extended precedence order")
In this case you can't satisfy all the properties
with the original code, you can't satisfy the second and third
that's why you have to switch the order
this makes me sad, even tho it's understandable
does it? If you're bumping into these rules frequently then IMHO you're probably using way too much inheritance to begin with
oh no this is the first time i saw this, was just trying random stuff
ah ok. Yeah, idk opinions will vary, I don't mind having the option of MI in python, but IMHO should be used carefully and sparingly, and probably avoiding repeated bases entirely
there's a reason why so many languages have very deliberately avoided MI entirely
nice for mixins and behaviors -- but no shared bases in these cases
Can anyone figure out why I'm missing a tab in this output?
address-family ipv6
{% for vtep_short_list_item in vtep_short_list -%}
no neighbor {{ vtep_short_list[loop.index-1] }}_EVPN activate
{% endfor -%}
!
-=output=-
address-family ipv6
no neighbor BH-MDF-EDGE01_EVPN activate
no neighbor BH-2-LEAF02_EVPN activate
no neighbor BH-2-LEAF03_EVPN activate```
this is not the channel
@deft pagoda where do you recommend?
People in general can't seem to figure it out.
this isn't a help channel
is that even python
python backend Jinja2 for the template
Sorry Neutralgood- this channel is for the discussion of python's base nature- what makes it tick why it works the way it does. Not for answering specific coding questions
You should take your question to one of the normal help channels. Like #help-apple
ok thank you
@vocal gulch please don't post random memes here.
sorry
hashing a string is O(n) for the length of the string, yes? So there's no point caching calls to str.count?
i'm not sure count and hashing are related?
isn't str.count also O(n)?
you want to cache calls to str.count?
yes.
but i think strings cache their own hash
how could a string cache its own hash?
wouldn't you have to hash the string to look it up in the cache's hash table?
if stored is None:
stored = hash(self) #or whatever, pseudopython
return stored
return stored
```?
hash(s) returns an int. The string holds onto that int.
so it gets stored along with the other attributes of the object? I suppose that's nice if you're using the same string object.
A string also caches its UTF-8 encoded representation.
Also, note that the hash is expensive to compute, but cheap to store.
If that's the case, you'd think they wouldn't want to compute it until it's needed, and only then cache it
Here's the string struct:
https://github.com/python/cpython/blob/7565586724692e2ad164d770af9675f7a261fe3a/Include/cpython/unicodeobject.h#L79
It's a little complex (since it will switch between 8-bit, 16-bit or 32-bit characters depending on the largest character value present), but the hash value is a member right there. Interestingly tuples don't cache their hash, because it was found to not actually be an improvement.
And https://github.com/python/cpython/blob/7565586724692e2ad164d770af9675f7a261fe3a/Objects/unicodeobject.c#L11794-L11801 is where the hash caching is.
Objects/unicodeobject.c lines 11794 to 11801
if (_PyUnicode_HASH(self) != -1)
return _PyUnicode_HASH(self);
if (PyUnicode_READY(self) == -1)
return -1;
x = _Py_HashBytes(PyUnicode_DATA(self),
PyUnicode_GET_LENGTH(self) * PyUnicode_KIND(self));
_PyUnicode_HASH(self) = x;```
Hey, I've got a curious question for you guys
Let's say I'm successful in building this new language. Modified python running in python. Let's say the time comes for me to translate it to C.
Cython, if I'm not mistaken, compiles Python into heavily optimized C
only if you're using C types
Right, okay
I'm just curious (which is the whole point of this venture) what I would do if I wanted to fully decouple the project from Python
use a functional language for a compiler lol
You say that as if it's obvious- I have no context
You would write a compiler, or a VM, for your language.
_> I used to think I was smart/an experienced coder. Not so much after talking with you guys
I suggest just beginning to learn a functional language, if only to learn to apply the concepts inherent to functional programming to python
writing a compiler is usually a very late int he game sorta project
and just from my cursory foray into haskell everyone constantly says it's great for writing compilers
You could write your compiler or VM in any language. You could even write it in your language
compiling is deterministic single input, single output tho so FP seems pretty perfectly suited to it
also compiling arbitrarily nested code seemsto require arbitrary recursion in the compiler without rly messy logic
Alright then. There are things I love about python, some things I don't, and some things from other languages I wish it had. If I wanted to build that, I'd research the grammars and implementations of all the languages involved, using them as blueprints, while ironing out friction points as needed. My VM would tokenize, parse, compile, and interpret the language according to the rules I'd write, while the actual functionality would be written in, most likely, C
Eh. If you've got eval or exec, or even import with Python-like semantics, you're taking input at various points during runtime.
but that's just conjuring up the compiler again
Not really. Not if you consider thread safety, or the need to expose variables from previously run code
ah so the compiler needs to "decompile" or at least access the namespace as if it's pre compilation
but isn't the sorta trivial with python bytecode?
See, that's what I'm thinking
My understanding is that it's all one big pot. The data is all there
but yeah eval exec and other pythonic conveniences will not be easy or even feasible with sky's dreams of executable compilation and runtime static type checking or w/e
executable compilation?
Compiling to bytecode is (relatively) trivial, but that's specifically because there's a VM to execute it. If you want a language without a VM, it would certainly be much tougher
well why decouple if you're gonna reinvent an existing wheel?
To learn of course!
lol makes sense
I can't blame ya cuz I focus on fool's errands more oftent han not too
I bothered to figure out what a metaclass is for godssake
Think about it. If I reinvent the wheel and rebuild python from scratch, think about how much better my python will be
In terms of both context and experience
Honestly if you want to make a language, you'd probably be better off making a few simple ones first.
...not at all cuz microsoft just starte dpaying big bucks to ppl rdy to improve cpython
Those are rookie numbers. You gotta bump those numbers
But no, in seriousness, I learn best through trial by fire. Always have
like all power to ya if you pop outta the woodwork with a superior lang but that seems unlikely for one person
And in no way am I trying to replace or even come close to rivalling python
have you ever gotten millions of people to spend thousands of hours using something you created?
lol yeah
I think you're underestimating the task. Try making a Lisp-like language first: it's much simpler, and will still absolutely be a trial by fire.
Or even one like nim
I know full well this project could take years, if it succeeds at all. Thats fine.
hell just make a brainfuck interpreter
On the topic of the static typing- I'm still not satisfied
lol u rly should use a functional language
I accept that my approach is wrong- you've made your point quite well
Good advice!
It's not just that - in addition to that, it's more likely to succeed if you build up to it gradually. You're more likely to burn out and give up if your very first language is as complex as Python.
Buy a book on compiler design, and let it walk you through building a language.
Guhhhhh
You're right, you're so right
I havn't read a book in years
Any suggestions?
damn I've got that book's existence burned in my memory and didn't even know it
You're way more likely to succeed if you don't try to reinvent the entire field of compiler design from first principles π
So in a nutshell, if I want this to go anywhere, I've got a lot of research to do and I probably shouldn't be doing it in python
You will have a much easier time if you start by researching, and the best materials to learn from will be textbooks.
I mean, it goes without saying I'm doing some research. Blog posts, articles, the occasional chapter from a textbook. Not exactly what you'd consider proper research. I usually work by refactoring some sort of working model, figuring out how each part works, and then making modifications as needed
But hell- the whole point of this is to become a better programmer. I suppose it's about time I got into the habit of reading a textbook.
There is a lot of online formal course material on compiler and language design
Blog posts can be almost counterproductive if you don't have a clear learning path. Books and courses can provide such a path.
Well guys, I appreciate all the help
I'll probably pepper you all with so many questions over the next year that you'll be sick of me XD
That said, I do have one little question I'd like to hear your thoughts on. I hate dunder notation. I just dislike how the double underscores look
If you had the chance to change it to something a little easier on the eyes, any thoughts?
Maybe
nondunder = someObject.init
dunder = ~someObject.init
I know its silly XD
give every object a special __ namespace and put dunders there
dunno, i like dunders -- when i was starting with python i knew that dunders were somehow important to the data model without knowing anything else about them
class SomeObject:
def __.init(self):
...
im not sure it's any better
in fact it's probably worse
Well, if we're going for broke
how about this: use lua syntax instead
class SomeObject:
dunder def init( self ):
...
In C++ there are equally magic names, but they stand out less. Instead of __init__ it's MyClass. Instead of __del__ it's ~MyClass. Instead of __add__ it's operator+. Etc.
Here's something I'm having trouble wrapping my mind around
Is this idea of going up by a level of abstraction. Like, if I implement my own version of a class in this language (using python, for example), how is that any different from just inheriting from a python class?
Your language exists independently of its implementation.
There are many implementations of Python - I know of at least 4 written in C, 1 in Java, 1 in C#, 2 in Python...
Your language is its semantics and behavior, not how it's implemented
cpython
pypy
cinder
pyston
ironpython
jython
stackless python
micropython
any others? do shedskin and nuitka count?
Semi-related: Is it worth it to try to reimplement a language in a different language just to have fun?
Brython too
I counted Cython in there as an implementation of Python in Python. It doesn't run it, but does compile it
CircuitPython, pycopy
i was wondering if there was a graalpython
apparently truffleruby is turning out to be very impressive
graalpython could be very interesting
(defclass person
((name :accessor thing-name
:initarg :name
:type string
:documentation "Person's name"))
(:documentation "A person"))
it's never too late to lisp
The question seems a bit strange, honestly. Classes in CPython are just implemented as heap allocated C structures and function pointers, but they're clearly very different from heap allocated C structures and function pointers.
"it's never too late to lisp" - Salt Rock Lamp
Words to live by
Very good point
Again, I feel that reimplementing a big fat language is a fantastic way to gain both experience and context. It can't not make you better at using sed language
is lisp oop really that ugly
That's good to hear. Ty!
Just in case you guys are curious, this is the lexer I'm basing mine off of (for now)
https://github.com/m-labs/pythonparser/blob/master/pythonparser/lexer.py
uglier
we really really need Common Lisp 2: Common-er Lisp
dunders are supposed to make you uncomfortable
imo
like other janky stuff
only use them if you are sure they're a good idea
python allows most things but most of it is uncomfy for a reason
yeah and how many months was it before you wrote a class?
no
I'm calling it uncomfortable if you don't know what it does
because it is magic
Dunder methods are not that scary. not scary at all
Each one corresponds to some top level syntax. Boom- dunders explained
Its not that they are some secret magic that needs to be used with care. They are backdoors into the syntax
but they make zero sense at face value
Psssssh
so it makes sense for them to inherently cause second-guessing via syntax
Sorry, that was kinda rude
no entiendo una verga
the only actual constructor in python is the calling of a class
other than u know defining classes and functions
technically
and I guess literals
i think at least half of classes i write are only dunder methods
I mean, my philosophy is to abuse the syntax to maximal effect. I use dunders like a sword. I can't count the number of times turning my object into an iterator or even just manipulating its bool value has saved my ass
but you know what all those methods do and feel confident using them
i rarely use dunders, other than the common ones
idk if u wanna save your ass with dunders
you should do rational stuff with them ahead of time
I mean, I suppose it's all a matter of taste really. Use dunders, don't use dunders. Whatever works, right?
and document it heavily
I think for getting into the language and the flow of things, it makes sense to ensure ppl are second-guessing dunder usage
just cuz they are super easy to abuse
I'm not syaing they shouldn't be used
just that it's very very easy to define them in a way that makes functionality of your objects totally opaque
i.e what if I defined comparison operators based on alphabetical oprering of any compared class instance's class name
that would use a few dunders and would seldom make any sense to do
i mean, if writing an implementation of some abc, most of your methods will be dunder methods:
In [44]: MutableSet.__abstractmethods__
Out[44]: frozenset({'__contains__', '__iter__', '__len__', 'add', 'discard'})
and that is the intended usage
especially cuz you're using an abc
so the second guessing is waylaid
and you probably have an idea of what those dunders should do intuitively if you know what an abc is
it's not specific to abcs though --- if you're implementing an object that wants to feel like it "belongs" in python, you'll be implementing a lot of dunders probably
yes but you should know what those dunders should do to feel that way
and that's how the comfy overcomes the uncomfy to make dunders great
and ppl who like uncomfy post their gore in #esoteric-python
myself included
you should know what any method does before you implement it, that's not specific to dunders
non-dunder methods have no implications or magic involved
they're just called explicitly
the only weird one there is setters and getters being overloaded but imo that's just a design improvement over other oop langs that make defining them for every damn attribute imperative
i'm not sure what point you're trying to make, but i've decided to disagree with it in any case
lol I'm saying dunder syntax is good
because it signals "this isn't a normal method"
to several different types of programmers
especially beginners and ppl who know just enough to be dangerous
That's a disingenuous argument, since it would be equally nonsensical without dunders. There's nothing in particular that makes if x < y any more error prone than if x.lessThan(y)
You can look at __lt__ directly as well
but it isn't ever explicitly __lt__ or any method name
it's a character <
it's an operator
it should have certain behavior
Right. Dunders are the mechanism by which classes register their behavior for operators
and even if you disagree with it it's very clear the design philosophy of python involves keeping operators somewhat pure in their functionality
but still permitting devleopers to do wtf they want
How so?
and to me that involves a level of friction in absence of restriction hence dunder syntax
have you ever read the debates about stuff like concatenation operator vs union operator for dict?
is this the start of a dirty limerick
lol I hope to be an oracle that speaks in dirty limericks someday
But it's ugly as hell. For example
someClass.__bases__[ 0 ].__init__
Was hoping to find an example of three or four chained dunders together but couldn't on the fly
Still, nasty jazz
that should be ugly, why are you accessing the first base like that?
if you know why, okay
do it
but that isn't normal python code
Oh, I know, totally pointless. Just an example
I'm not seeing whatever friction you see. Python wants classes to be able to register methods that will help the class support operators and various other operations. Those methods need to have names, and those names need to not be names that might conflict with a method the user otherwise would have wanted to have. Thus, dunders.
and that's why they're dunders
that is a pointless thing to do and you should second guess it
Of all the things to have a phobia for
the friction exists in the mind of skywalker
reminder that __init__, __repr__ and __str__ exist
and for good reason
they are prone to the dark side,skywalkers
and you should be damn sure those three do what they should do!
speaking of overloaded operators --- this was deferred until 3.8 https://www.python.org/dev/peps/pep-0535/ but i can't find any recent mention of it
The only thing special about dunders is that they're a namespace for the special things, so that the special things can exist, and can't be confused for regular class functionality - and so that new special things can be added. If there was no namespace for these special things, adding any new special method would be a backwards incompatible change
yeah
Personally, I prefer the 'multiple buckets' approach
Dunders go here, other methods go there
No conflict
it's just they are also uncomfy aesthetically lol
Fine, but that bucket still needs a name
That's entirely subjective. I don't agree.
well skywalker seems to thinkt hey are
As for access (if we're talking about possibilities, not reality)
class Something():
dunder def init( *args ):
...
def init( *args ):
...
something = Something()
something:init( arg1, arg2, arg3 ) # dunder
something.init( arg1, arg2, arg3 ) # non-dunder
and I was just arguing for that being a good thing
if ur comfy with dunders that's well n good
: vs . is very-subtle, us sight-challenged people will die
I think that's much harder to read. That one extra dot that says to look an attribute up in an entirely different namespace is... Challenging.
and the wya they are defined the same way
Grosser than
__init__
Much, yeah.
guys I need a legend to help me with my school hw for python
too comfy at definition too ambiguous at retrieval
Hey, I was just throwing out an idea, food for thought
also no backwards compat with methods called init which make sense sometimes especially when metaprogramming around __init__ methods
this is a discussion channel, not a help channel -- see here: #βο½how-to-get-help
Just trying to demonstrate that with one extra keyword and one extra operator, you can implement non-conflicting namespaces and still designate, visually, a seperation of usages
but you don't need either of those things
and that's one more bit to add to cognitive load of parsing the lang
Again, with respect, psssssssh
instead of just being a darn method that is explicitly uncomfy
I'll bet they said the same thing about decorators when they were first proposed
why would you even have a dunder keyword when dunder means double underscore lol
Alright then, what about 'syntactical'
eww
or magic
even worse
Or whatever, doesn't really matter
if you think of a better thing write a PEP and implement it and do a pull request on the cpython source haha
I think dunder works fine, really. A respectful throwback to the old days, when underscores were king
i really like dunder methods --- in other languages it's not always obvious what the "special" methods are --- even a complete beginner to python knows there's something significant about __init__ without knowing anything else about it, because why would it have such a funny name. these methods really stand out, and for good reason
Fair
python was my first lang and dunders were mysterious and scary in a very good way
O.O it is seriously spicy in here tonight!
I hope its not rude of me to say, I love it!
it was immediately obvious I had to learn more to use them correctly but there wasn't more syntax to learn than how they're named
lol I gotta sleep but ya its been good
I was the only actual programmer in my whole cohort (it's an interactive media design program at the local community college)
So I couldn't even talk with my classmates about this sort of stuff
im the only python nerd at my community college rn π
but the uni im going to does python as a main thing luckily
#feelz β€οΈ
There's definitely other ways to get the namespacing, yeah. Python, like C, went with an approach where the special stuff owned by the language is alongside the regular stuff owned by the application code, differing only in naming. Other approaches work, though.
C reserved two namespaces for the language/implementation/standard library: things starting with an underscore followed by a capital letter, and things starting with two underscores. Python reserved one, things starting and ending with two underscores.
javascript shoved everything into the same namespace then added another one then added symbols haha
Guido's choice of namespacing for special methods in Python was presumably heavily influenced by how C reserves names for the implementation, come to think of it
I mean, I'm probably going to go the explicit route, have a namespace for each of public, private, and dunder
I personally don't like the idea of people being able to touch the so called private attributes of my classes in Python. If they want to screw with the functionality and such, they can subclass
Godly I've got one more for you
I'm self taught and my coding style is proudly unusual. But that doesn't mean I don't want to know the proper way of doing things
If I have a big and cumbersome class, like a lexer, I usually like to break it into many smaller classes each in their own modules. For example
- Lexer
- init.py
- Lexer.py
- LexerAbstract.py (the lexer metaclass)
- LexerConstructor (initialization and properties)
- LexerWalker (iterator protocols and the main body the lexing process)
... (other classes devoted to specific portions of the lexing process)
Bad form?
Not how I'd do it, personally. Needlessly spreading functionality across many classes is Java-esque and enterprise-y in all the worst ways.
you can also create circular dependencies if you break up your class too much into separate files
which i've done to myself enough times
Python's data model makes privates pretty much impossible to implement. The idea behind privates is that things "inside" the class should be able to access them, but things "outside" the class shouldn't. But the data model doesn't enforce any ownership of what's "inside" a class, and you can add new methods to a class from outside the class at any time.
You'd have to remove a lot of language features before your privates would actually be relatively private.
anyone ever seen changing types in the wild?:
In [51]: class A:
...: def say(self):
...: print('hi')
...:
...: class B:
...: def say(self):
...: print('hello')
...:
...: b = B()
...: b.__class__ = A
...: b.say()
hi
I didn't know this was a mutable attribute
oh, that gives me an idea
Cooooooool
being able to swap multiple methods at a time of some object i can guess is useful for testing, but i think i can find other neat uses as well
You could abuse the crap out of this XD
Also, regarding your concept of ownership, I've always found it odd that a class doesn't exist during it's definition. I do understand that, as it stands, the class collects everything in it's definition space and then initializes itself, but it seems odd they didn't simply inject the uninitialized class instance into the definition space. By the nature of the fact that the parser knows its in a definition space, a reference to the class or at least its context exists somewhere
As for access to private variables, I guess you could have a designation on functions within the class as being either 'native' or 'foreign'. Anything that was present in the class at the time it was instantiated is native, otherwise, foreign
That would let you leak a partially initialized class out and start using it.
So, the way classes work is that it calls a metaclass with it's environment, bases and such and then the metaclass gets called and creates the complete class. A half initialised class never exists, it's just a dict and a tuple, which once full are made into the type object
Yes, fair point
But inside methods, you can use __class__ to get the class the method is in. It's a parser trick iirc
I think you underestimate how much this would cost you. It would break pickle and the copy module, and debuggers, and lots of class decorators, and the ability to perform white box unit tests, and I'm sure many other things that I'm not thinking of.
It's all about context though, right? Knowing whether or not the accessing agent is or is not an instance of the class
Right. In all of those cases, it's not.
And the accessing agent could be a module (or module-like object, in the case of an eval or exec) or a function/method
(Just asking for the sake of understanding)
In fact, it would break getattr and setattr entirely. They currently only need to know what object they're accessing, and what attribute from it. You'd need a different contact where you pass them an extra piece of information, who is requesting to get or set this attribute, so that they can choose whether or not to allow it.
But deep deep down in the interpreter, that data is available
At the moment the data is requested, the scope of the operation is visible. Could the identity of the scope's owner not be passed?
Ahh, I see
You don't just want to know the owner, you want to know whether the owner is privileged to access the private
As we're talking, I'm getting a clearly picture of what it is I actually want
Very few languages have access modifiers that are actually always enforced. Java, C#, C++ all give ways for other classes to access private attributes. The only thing python does differently is that bypassing it is much cleaner.
And, things like pickle and copy.deepcopy and debuggers access attributes of arbitrary objects as part of doing their normal job. Making it so that there are attributes they can't access will cripple them.
I want a language that support privacy and type enforcement, top-level syntax matched to dunder methods, iterator protocols, indentation based code blocks, python style importation, multiple inheritance and metaclassing
And a a few other python bits and boops, as well as an undefined object and readonly attributes, and javascript's ability to call simple objects into existence on the fly (as a bonus. Not super important)
And, for love of god, arrow notation
The prefix _ convention is about as strong as access modifiers get in general purpose languages exactly to make metaprogramming easier
And if that's what I want, I should use python as a roadmap but not a scaffolding- and keep my mind open to how other languages are implemented
Plus, do a butt-tonne of proper research
If you want to look at statically typed python, there is rpython
Sounds like just a strongly typed python. Which fair enough if that's what you'd like, but making python strongly typed would make lots of python's features difficult/impossible to implement and use.
What's arrow notation?
(lambda, arguments)=> lambda body
No idea how you would make it useful in python, since blocks as expressions don't look all that nice. Just look at nim for a language that has all statements as expressions and the same indentation based syntax as python
Well first off, you wanna talk ugly; lambdas are ugly
Also your arrow notation is a little off. Pythonically it'd be
( *args, **kwargs ) => { body }
Arrow notation is hugely useful for callbacks and they make life a lot easier in event driven situations. Basically anything that runs in a loop for the lifespan of the program
Additionally, as with any function defined within another function, they can see the scope of the function they're embedded in
I once wrote this
As a kinda joke
Won't run on the bot because of frame shananigans, but works most of the time.
Consider:
def someMethodThatTakesACallback( callback, *args ):
return callback( *args )
def callback( a, b, c ):
return a + b + c
result = someMethodThatTakesACallback( callback, ( 1, 2, 3 ) )
versus
def someMethodThatTakesACallback( callback, *args ):
return callback( *args )
d = 4 # added
result = someMethodThatTakesACallback( ( a, b, c ) => {
return a + b + c + d # added d
}, 1, 2, 3 )
In my opinion, cleaner. Its also well established in a lot of other languages, so it's not exactly a ground breaking concept
Sorry, I'm gonna make one more modification- one sec
The anonymous arrow function can see the scope it was created in, and therefore, can access 'd' even though its being kicked around like soccer ball
I find block lambdas kinda overrated in a language like python, with first class functions.
@takes_a_callback
def result():
Code
```why not use a decorator
That's what most event driven libraries use
You can do it inside function bodies if you need closures
Could I see an expanded example? I'm having a hard time visualizing what it would look like in the wild
Here, one sec
element.addEventListener( 'load', ( event ) => {
print( event.type )
} )
Say for a moment we were in a python based web browser, where the elements in the dom were python objects yada yada yada. Both the element itself and the callback you're providing are being used on a variable level, outside any type of class definition
How would decorators be used?
def main_of_sorts():
element = ...
@element.eventListener
def load(event):
print(event.type)
#or
@element.eventListener('load')
def my_event(event):
print(event.type)
I will admit, that is rather sexy
I wouldn't exactly say better, but still rather elegant
I do find it nicer than the JS approach, though both have the same issue callbacks have which is you can easily get into callback hell and just end up with massively nested functions/arrows.
i agree for the most part, although there are a lot of cases where writing def badly breaks the visual flow
i've done similar decorators for binding methods to attributes, e.g,:
@bind('width', 'height')
def _resize(self):
...
π I don't see any reason the two couldn't coexist happily. There will be cases where both will work interchangeably, and cases where for whatever reason one trumps the other
there is also the kivy approach which uses on_thing named methods and just calls them dynamically with getattr
yes, i like that too, but it's pretty implicit
you can bind things manually in kivy though
my_property.bind(...)
I would rather not have 2 very similar, yet slightly different tools. 4 string formatting options speak enough about why that is a bad idea.
I think that's a bit of a different case
fstrings are unambiguously the best, with .format() as a distant second. That there are 4 is simply a matter of legacy baggage being carried around
(In my opinion)
format has some nice use-cases
Like passing **someDict
yes
there are cases where you want % formatting for strings that contain {}, and string.Template if you want to pass format from users
Which, I think, feeds in to my original point
Just because the syntax is there doesn't mean you have to use it- but when the time is right you're glad it's there
And, lets be honest, on the decoratee's end decorators are great, but writing them can be a pain
At least for me and I'm sure for a lot of novice programmers. Arrow notation might very well be easier to wrap your mind around- if perhaps a shade less powerful
As I think about it, actually, they might operate similarly in some contexts but they are completely different animals in others. Arrow notation is a no frills multiline lambda, while decorators are wrappers around functions. Their nature's are quite different (not meaning to spam, just thought it was a point worth making)
the issue is you have 4 extremely similar features which have slightly different usecases and can have surprises (user defined format strings for .format are often unsafe).
the only case in which you need a multiline lambda is as a callback
and decorators have handled that well enough in most cases
Soooooooo not true
Multiline lambdas are as useful as you make them. The only reason python doesn't have them is that sorting out the syntax is a bit of a pain (it conflicts with a few already well established aspects)
But as a general rule, they are a perfectly useful tool in all sorts of contexts. Filters and maps for example
hehe, with a bit of creativity, I'll bet you could even rig up some sort of 'refresh' method attached to them, such that they replace their original outer scope with their current outer scope, wherever they may be, and you could have traveling methods that could see outside themselves (don't read too much into this, its just a thought)
anon = ( a, b, c ) => { return a + d, b + e, c + f }
d, e, f = 1, 2, 3
print( anon( 1, 2, 3 ) )
>>> ( 2, 4, 6 )
def someFunction( anon, a, b, c ):
d, e, f = 10, 20, 30
return anon.refresh( locals() )( a, b, c )
print( someFunction( anon, 1, 2, 3 ) )
>>> ( 11, 22, 33 )
result = map( ( value ) => {
if value is True: return 'The value is True'
elif value is False: return 'The value is False'
else: return 'The value is None'
}, [ True, False, None ] )
Just posting this for others who might want to check out our discussion in the morning. I'm curious what it is you're typing
Well it's just an example π
this.sessions = this.sessions.filter(session => {
try {
session.webSocket.send(message);
return true;
} catch (err) {
session.quit = true;
return false;
}
});
```there is this example of a filter multiline function, and while it is pretty, is it really a meaningful improvement over the more conventional.
```py
for i, session in enumerate(sessions):
try:
session.web_socket.send(msg)
except ClosedError:
session.quit=True
sessions[i] = None
sessions = [session for session in sessions if session is not None]
Like yes, there are cases where they work out nicely, but is there a case where they are enough of an improvement to be worth it. You can always come up with a solution that is adequate. Python doesn't really have the facilities to support things like java or JS fluent list manipulation. map is already often near illegible and imagine having multiple of them nested with filters inbetween to emulate .map .map .filter. Is .map .map .filter a nicer solution? Probably. But the thing stopping python is not multiline lambdas, it is being inherintly designed around statements in tradition of a specific algol version which I didn't memorize. While you could pivot the entire language, it is probably easier to just use a whole different language. Nim for example has all the features you need for this and some extra.
With respect, all I ever hear from you guys is 'no, no, no'
Which is good, it keeps me focused
It's a bit depressing though π Here I am trying to apply creativity and intelligence to push the envelope- and challenge preconceived notions
And a bit of optimism would, I dunno, would be nice as I embark on this journey
What you call pushing the envelope, I call bloat.
keep in mind that python is meant to be simple, so any feature must really improve on things to be considered. Despite what functional programmers try to tell you, heavily expression based languages are harder to explain and learn, so keeping the imperative spirit is pretty desirable. If you look at scratch, logo, basic etc. they are all even more imperative than python
Python is far from simple. It's worse, it's deceptively simple. Specially if you're first learning to code.
it is very easy to learn, which is really what you need
It's easy to learn but also endlessly deep. Calm but endless waters
every language is endlessly deep
You'll never have too much trouble with any particular aspect of Python, but you'll never be able to find solid ground either
there is always deeper to dig, more libraries and conventions to learn.
be it JS, java, C#, VB, scheme, Elm, haskell, C
you can't have a finite language, computing is far too vast for that
I thought so long ago, but now I'm convinced it's not. Python gives the air of being simple, but the realities of immutable vs mutable data structures, passing by value vs reference, bad error messages, abuse of try/except statements, etc. These hit first time learners early and it hits them hard.
That without mentioning the non-language stuff, like managing dependencies and versions.
You know what I think my biggest take away tonights discourse has been? Everyone has their own way. I can build or use whatever I want to build or use, and so can everyone else.
π That said, I'm gonna get cracking! I think I still have a few hours left in me
error messages are improving, immutable vs mutable is a thing in every language, you always pass by reference in python. Like yes, it isn't the absolutely perfect beginner language, but nothing is really better
immutable vs mutable is a thing in every language
But in a dynamic language like python they're particularly confusing to beginners.
any type system that is expressive enough to be useful is a massive learning curve. What is the type of list.append? What about list.__add__, ...
if you look at simplified languages like scratch, basic, logo, they are all dynamically typed, since that is one less thing to learn
I am not claiming there are no pain points to python, but I don't see a better option
I started with pascal, and it took me quite a while to even get to something. Python learners here are much faster with starting out.
and pascal was also meant to be simple to learn
Except that's not true, you still have to learn types, but the language hides them from you. How many times people post questions where they don't understand why None has no attribute 'upper'. How many of this people would have a problem understanding why this C code doesn't compile
char c = 'a'; sqrt(c);?
Pascal is a good language.
char c = 'a'; sqrt(c); this compiles, but that is beside the point.
How many people would be confused by the type even for something as simple as lists. How many people learning C in uni spend hours upon hours struggling with arrays.
One of the most common questions of java learners is "why is my program with lists not compiling"
Type systems look nice and simple until you need anything but scalar types. Even in pascal, arrays are really annoying to get right.
I'm with Lak on this one- Python is by far the least worst option when it comes to learning
It's clean, sensical, and opens itself up at a reasonable pace
is it possible to teach type systems well? Yes
Do people have access to such teachers? No
I'm not defending C as a learners language that was just an example of how types help first time learners (even if the example was wrong), while dynamic typing creates confusion.
I just finished my first year in college and we had a coding course. I'd been coding almost a decade at this point so it was a total snooze, but
Watching those poor bastards (my classmates) struggling with the anatomy of even a basic C# assignment was horrid, and there is good reason for the struggle. Type declaration and conversions, hideous for loops, privacy, not to mention it throws you directly into classes without any preamble
Python just has less stuff, on its surface. Its all still there but you don't have to worry about it until you're ready
is mutability/immutability that big of an issue? It should be clear after a proper (short) explanation
I firmly believe that if you're going to teach a C-type language to absolute beginners, you start with two weeks of python
Fewer tears that way
But suffering through a code that does not compile is easier than suffering through a code that runs and crashes. Most first time learners dont' have the know how to debug runtime errors at all.
When learning the scripts will be short enough that runtime and compile errors are almost synonymous as they will pop up immediately when running
Imagine if someone were trying to teach you the guitar. They can explain it to you until the cows come home. Once you get it in your hands, the conceptual knowledge barely makes a dent in the terror you feel, and the difficulty
You've got to feel it
I don't think this analogy applies at all.
That would be the equivalent of teaching someone to code with pseudocode.
Says the guy who most likely plays Python like first-chair plays the violin
Programming absolutely is like playing an instrument, and is absolutely is something you have to feel. No one can tell you how to do it- you and the language have to do a dance together
Until eventually you're reading each other without thinking
Getting compile errors is practice, not theory.
one bug a statically typed language would fix is reusing a variable name, that is true.
But having None instead of a string means that you at some point misunderstood how your code flows. In a statically typed language, you would likely have null or "" in it's place, and may not even get an error at all. It may make you realize that something is wrong, but errors in logic are often padded over with whatever the hell makes the compiler shut up.
Beginners often feel that the compiler is wrong. you can see it in C and C++ best where people will just put & and * until the compiler stops yelling at them about type errors, even if they invoke UB. Especially if their line of thinking was correct, but failed to transfer it to code.
Object destructuring (or, named-destructuring might be a better term)
You can do it for iterables, mostly, but named would be great. It could work on dicts and by extension, objects
boy do I have news for you
>>> from dataclasses import dataclass
>>> @dataclass
... class A:
... a: int
... b: int
>>> match A(a=34, b=5):
... case A(a=a, b=b):
... print(a, b)
34 5
>>> print(a, b)
34 5
```python 3.10 does have just that
YAYYYYYYYYYY
it is one of the more controversial features, since patma is inherintly complex and the syntax is very mediocre
patma?
pattern matching
Ahh, thought so π
but it does solve a lot of problems that came up in python libraries
if you look at the public API of quite a few libraries, you will see like 8 lines of normalizing arguments followed by 3 lines of actual logic
Yupppppppppp
I mean, I have no idea what you're talking about, except that arguments can be a pain the ass
there is this little if in almost single turtle function
def fun(x, y=None):
if y is not None:
pos = Vec2D(x, y)
if isinstance(x, Vec2D):
pos = x
elif isinstance(x, tuple):
pos = Vec2D(*x)
@upper haloconsider asking in a help channel (read #βο½how-to-get-help to see how to get one) or in #game-development
even after that, it will be a few months before libraries port and a few more until the mainstream ports
do you know if there will be backports for pattern matching like with f-strings?
I do not
i've seen a module that provides something similar to it, but isn't fully compliant with the pattern matching pep yet
run-time type validation be a lot cleaner to write with it
On that note, I've been thinking long and hard about automated runtime argument validation
I've heard the arguments against and so I'm not holding my breath, but it occurs to be that one could apply regex to the problem
I'm a bit too tired to go into it right now, now that I think about it, but as I continue to ponder over the next few weeks, I'll show you guys what I'm thinking
I see Python learners do that too tbh
especially when you don't even start learning Python for Python, but Python for DS/ML
π₯΄
yeah, not trusting the compiler is pretty universal
I was interviewing a couple of days ago
and the candidate talked about how she learnt that a lot of programming is identifying the mismatch between what code actually does and your mental model of what it does
I've had people explain to me that validating simple types like strings or bools is quite simple, but complex or compound or component typing is difficult.
def func(
argument : Callable[ [Iterator, Callable], Generator[int, float, str] ]
):
...
Not so simple.
Checking this manually would be a pain, and there might not be a way to cover all the edge cases. But if you instead converted objects into patterns, using letters or keywords perhaps to indicate their various characteristics, you might be able to apply regex to ensure that needs are met. I havn't formalized any of this, so it's all just spitballing at this point but
# A class's 'encoding' would consist of Classname[:methods[:arguments[:returntypes]]]
# arguments/returntypes only applies if class is callable
class Object( ):
# this class would encode to Object:next|call:[.+:next, .+:call]|[generator:int|float|str]
def __next__():
...
def __call__( a : Iterator, b : Callable ) -> Generator[int, float, str]:
...
If you can encode both the functions signature, and all of the arguments provided to it, you could do a regex check to make sure they match. Steps could be taken to allow multiple possible types, specific numbers of a given type, etc.
Just an idea I had π
why not use a statically typed language @ this point
you mean attention?
I like the limits of static typing more than those of dynamic typing tbh
no, not the ML transformer
the monad transformer is a construct in functional programming
that allows you to compose monads
yup
Night all
first time learners don't have the know how to debug anything
I've seen enough C/C++ questions on SO to know that when there's an error, its much much harder to debug than any dynamically typed lang
compare an error traceback with a segfault
yeah, C/Cpp runtime error reporting is atrocious, java does pretty well though
there is a python PEP to allow reporting not just the line the error is on, but also which column it happened on
so you would see pretty exactly what went wrong even in long lines
if it can be implemented well, that is probably a PEP that will be accepted
!pep 657
whats the difference between your proposed method and just comparing the stuff that you're encoding normally without any string manipulation
<@&831776746206265384>
!ban 810292495199502375 Take your trolling elsewhere.
:incoming_envelope: :ok_hand: applied ban to @stuck beacon permanently.
@paper echo re: #weekly-topic-discussion Some people define a framework as a library with inverted control, i.e. instead of you calling its functions, it's calling the functions you provide in a specific way.
which sort of makes sense
i like that idea a lot
Well for one, and I might be wrong here, using a regex statement will build your DFA for you
It'll automatically build a heuristic that will cover all relevant possibilities quickly. Also regex itself is fast and 'trustworthy', which is to say, not experimental
Does anyone know the rationale behind types.Union supporting isinstance and typing.Union not and why you shouldn't just use a tuple?
you can just use | in 3.10
- they both work
instead of a Union
- you can't just use a tuple because type checkers don't treat tuples as union types
I mean, you can, nobody is stopping you
but it won't work with the tools
Why it is that way? I don't know. Maybe ask in typing-sig
for x : A, y : B, the tuple (x, y) is (isomorphic to) an element of the cartesian product A x B
basically, the notation is already taken by another well-established concept
This is an update I think, IIRC was not a feature in earlier mypy
I really want to thank you guys
Last night really got my engine revved up. You've all given me a lot of perspective, but also emboldened my sense that I'm going about doing what I'm doing for me, and I can program it however I want. I needed the confidence boost
π
...which is happily ignored in the annotation syntax π
as in, annotations don't support (A, B) to mean Tuple[A, B]?
yeah
maybe it leads to parsing ambiguity? it'd be nice for sure.
what ambiguity? it's totally valid syntax anyway
maybe there's some requirements for type hint objects to have some special fields?
yeah idk, it's a good question. maybe they just didn't think of it, or didn't think tuples were special enough
oh, you know what? i bet | is implemented with __or__
"we can implement it with existing techniques instead of adding a special parsing case" seems on-brand for python
in other news:
def f(x):
x += [7,8,9]
return x
data1 = [1,2,3]
data2 = f(data1)
assert data1 == [1,2,3], "This sucks :("
you mean data2 = f(data1)?
yes, fixed
!e
def f(x):
x += [7,8,9]
return x
data1 = [1,2,3]
data2 = f(data1)
assert data1 == [1,2,3], "This sucks :("
@grave jolt :x: Your eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 8, in <module>
003 | AssertionError: This sucks :(
yes, i hate it
yeah, it's a
moment
why?
naively, i think most people would expect "shallow copy on write" semantics here
so you would rather have all augmented arguments not modify self?
isnt that exactly what the normal operators are for?
def f(x):
return x + [7,8,9]
what we mean is that it's strange that x += y can behave differently from x = x + y, in particular the += on lists.
I don't think it's strange at all
exactly, so you wish for augmented arguments to always be name reassignments
Like, imagine explaining it to beginners.
"x += y just means x = x + y, except it doesn't"
yes
When you think about what's going on under the hood, it's a different process
thats a good point actually, there's no point in having augmented operators be special for mutables
Or maybe I'm wrong. But it seems perfectly sensical to me
I don't care how it's implemented. In most cases, x += y means x = x + y
@warm kernel :white_check_mark: Your eval job has completed with return code 0.
001 | (1, 2, [3])
002 | 'tuple' object does not support item assignment
003 | (1, 2, [3, 1])
You don't care- but the machine does
methods already exist for mutating why not maintain consistency in +=
thats the point fix is trying to make
The semantics of the language aren't concerned with the machine, not sure what you mean.
On lists, x += y mutates the list (same as x.extend(y)), which goes against the expectations of x += y being the same as x = x + y for every other object in Python, and for many other languages (where those are literally synonyms).
Mutate, as opposed to the creation of a new object?
Yes.
I suppose that is a bit odd. Do think it's for performance gains?
If you want to mutate the list, there's already .extend
turns out += actually isn't syntactic sugar for addition π
at the same time, how many beginners would write
for a in b:
c += [a, -a]
```and get O(n^2) loops. CPython even takes care of it for strings in cases where it doesn't matter
And if they made the choice to go with extension over the creation of a new list, probably for performance reasons, then I'm going to have to trust them
I think not making beginners confused is more important than making them write O(n) code. Or are you going for the CO2 argument?
it is just odd because it is inconsistent with tuples for example
yeah, that's a fair point
inconsitent how?
oh the mutable vs immutable thing yeah
a = b = []
for i in range(4):
a += [i]
#vs
a = b = ()
for i in range(4):
a += (i,)
I'ma ask a totally off topic question over in off topic- but I'm mentioning it because you guys are all smart as ballz
even taking into account the different possible meanings of 'balls', balls aren't particularly intelligent

Each one is under a buck


how does x = x + y behave differently than x += y for lists
because of the mutation?
it's a lot more efficient that way
and unintuitive
not really, not to me anyhow. In most languages I can think of that do something like += for a mutable type, you have mutation
really? examples?
C++
Kotlin
it's really the other behavior (creating a new object) that's not intuitive, if you think about it, because you would expect += to correspond to a dunder method
and you can't reseat a reference in a function, in python
so that means that the kind of += you want would need to involve some kind of magic
And for integers (which are immutable) it does
Whereas a mutating += can actually be implemented directly as a dunder method
a += b is
res = a.__iadd__(b)
a = res
it wouldn't work fine for list otherwise, since it would elude setattr and setitem dunders
sorry, I don't follow
It would work for extending a list, but not for setting an item in a list. lst1 += lst2 would work, but not lst1[0] += lst2[0]
is having a class within the init of another class an antipattern? It works, and part of me thinks its reasonable as it's only used within the scope of the init, but I'm not sure
eg:
class C:
def __init__(self):
class D:
def __init__(self,a, b):
self.a = 1
self.b = b
self.x = D(a = 2, b = 3)
usage:
c = C()
c.x.a
c.x.b
etc.
why not define the class outside the init?
didn't work outside the init
π
you mean outside the class entirely @flat gazelle ?
yeah
idk, bc it's only used in the class within the init π€
that's fine
like sometimes we'll leave a function within a function if it's only used there ? why not with a class like this
if you just need some attributes, you could also just go with a SimpleNamespace
never heard of that - i just wanted to have class.data.raw, class.data.interim etc in metadata class
oh yeah, simplenamespace is probably the way to go then (or a dataclass
I'm trying to use dataclass, but i still have __init__ π€ maybe i'm using them wrong
if you need an init in a dataclass, it either shouldn't be a data class, or use __post_init__
but honestly, I would just put the class outside
no idea what i'm doing really so it could be anything
it's fine to have some extra classes hanging around
fair enough
you can prefix it with _ if you don't want it as part of the API
api is a strong word for this thing : ' )
i'm just trying to get all the metadata into a single location, atm the class doesn't have any input to the __init__, other than self, but when creating it with x = Metadata() it didn't really work as well, and using __init__ made it work π€·ββοΈ so i used that
Java, C#, C, JavaScript all have x += y <-> x = x + y
whatever params are defined in __init__ should be passed to Metadata()
@dawn arch a lot of them are just written in - it's stuff like the project root / data path / storage location etc
i mean, if you have __init__(self, color, number) you need to call Metadata('red', 8)
https://stackoverflow.com/questions/6366231/why-not-overload-operator-for-stdvector
In C++, += isn't overloaded for vectors, or other collections AFAIK. Although I'm not a C++ programmer.
i know - i'm saying that i don't have that
i wouldn't define a class inside the init of another class, definitely smells to me
Keep in mind if you redefine the class every time the attributes won't be of the same type
>>> class A:
... def __init__(self):
... class B: ...
... self.x = B()
...
>>> a1 = A()
>>> a2 = A()
>>>
>>> type(a1.x) is type(a2.x)
False
>>>
>>> isinstance(a1.x, type(a1.x))
True
>>> isinstance(a1.x, type(a2.x))
False
@sacred tinsel hm interesting, not sure where / why this would matter, maybe it would somewhere thouhg
need to dig in a bit more
I'd consider it unexpected, unless there's a reason for the classes to be built dynamically e.g. depending on some param to the init
For example, if you annotate the attr:
>>> class A:
... x: B
which B? each isntance has its own
.. this is hypothetical because you can't annotate it for B isn't visible π
I'm doing so much wrong i'm sure π
i'm putting the metadata into a class, then am frequently doing meta = utils.MetaData() and using that in the module π€
i'm trying to make all the stages independent though , and am running them as a pipeline, couldn't really find much on that kinda thing
@grave jolt in C, that's because = itself is also mutation
you have the equivalence the wrong way around
= in C and C++ is not the same as = in languages like python, Java, etc
+= and = in C are equivalent because they both mutate
Well, yes, C has completely different semantics.
My point was that in C, x = x + y is the same as x += y
But C doesn't have operator overloading so it's not terribly interesting to discuss. the same with Java.
Yes... because they both mutate π
not necessarily
not sure what you mean by that
operator= in C is a mutating operation
but anyhow, again, in languages without operator overloading having these discussions is somewhat pointless
If it's a stack variable, e.g. a number, there isn't really a difference between mutation and reassignment
Okay. C# has operator overloading, and it doesn't let you overload.+=.
Does C++ redefine += for some objects to mutate them?
well, that's the thing, when you are only discussing primitive types it lessens these distinctions, and makes things seem more similar than they are
It doesn't "redefine"
it defines
+= for strings in C++, for example, appends/extends
similar for /= and +=
I was against augmented assignment mutating till I saw this convo and looked up the PEP
their rationale is pretty good
Hm, okay. += mutates strings.
nothing mutates strings
in C++
we're talking about C++ momentarily
in ruby you can mutate strings
oh
also Path objects
the signature that operator= is supposed to have, and given ownership semantics in C++
+= can never really do anything except mutate
so it's not really allowed to return a fresh copy at all
isn't it just syntactic sugar?
Okay, in C++ some collections implement += as mutating. It also has quite different semantics from Python.
My point still stands that in most languages (whether they have operator overloading or not) where += exists, += is just syntactic sugar for +.
My point stands that in most languages where += on mutable things exists, it mutates them π
what are those languages? C, C++ and Python?
And Kotlin
The only counter-example I've seen is C#, if C# actually has + for mutable collections
which I'm not sure it does
So, I've basically seen zero examples where x += y, if x is of a mutable type, would not mutate x
Kotlin btw actually lets you have your cake and eat it too, although some people probably don't like the ambiguity
If x is a val (non reseatable reference) to a mutable type, += mutates
If x is a var (reseatable reference) to a non-mutable type, += creates a copy and reseats the reference
ok that's cursed π
It's actually pretty fantastic
it adds quite a bit of convenient to read-only interfaces
But okay, there's still no example where x += y, where x can be mutated, does not mutate x. Python's list is mutable, so that's why it's intuitive to me that x += y mutates it
Realistically I was coming primarily from C++, and in C++ that's the only way x += y can work, so I'm sure that's the main reason it was intuitive to me
