#internals-and-peps
1 messages · Page 153 of 1
Yes, since it's an abstraction over a string I would expect that adding two of them together would result in a path inhabiting both
Honestly I'm surprised they added an operator at all
arguably it's an abstraction over a list of strings, rather than an abstraction over a single string (this is where @visual shadow and I disagree)
in other news
but yes, it's an abstraction over either a string or a sequence, depending on how you squint at it, but either way, + is used for concatenation for both strings and sequences
python2, you never truly took the print statement with you
They originally wanted to use []
that's interesting - I can kinda see the metaphor for that
it's "descent"
though I wouldn't have thought of that metaphor myself
Yeah I would actually kind of like that over /
Path('/home/user')[Path('Downloads/)]
Grab the Downloads folder inside /home/user
yeah. Hm. I wouldn't have thought if it, but I sorta like it.
but the fact that I wouldn't have thought of it probably does mean it isn't intuitive
it's super clunky. compared to either + or even /
Yes that's the issue, it is clunky
so ig it makes more sense for the latter
More so than /
Logically, because programs work with logic, it intuitively makes sense.
Path('Users') / Path('me') kinda makes sense but might as well use path.join or whatever
The /home/user folder is a container of folders, out of which I grab another folder (item).
that makes sense, but the metaphor falls apart a bit with Path("/home")[Path("me/Downloads")]
So they explicitly didn't inherit from str bc they didn't want to be able to concatenate with a string that wouldn't lead to a valid filepath. So it's almost like they didn't want these things to look or be expected to act like a string in anyway
why do you need to index Path('/home') with a Path if you're just making another path? why shouldn't it be just
Path('/home/')['me/Downloads']
user = Path('/user')
downloads = user['downloads/']
print(downloads['some_text.txt'].read())
lol
@raven ridge how do you feel about Keras using __call__ to represent connecting nodes in a deep learning model graph
I don't have to agree with you just because the steering council at the time made that decision - I don't agree with them either.
sure, but not what I was driving at - the metaphor still falls apart with Path("/home")["me/Downloads"], because if Path("/home") behaves like a collection, the thing that it behaves like is a collection of files and folders, which wouldn't contain sub-subdirectories
but this is all stupid anyway, can you not just put it into one Path constructor all at once? and if you don't know one of the paths or whatever just use an f-string
I don't know anything about it - what's that look like?
I do this very often
basically like this:
layer_a = Conv2D()(input_layer)
layer_b = Flatten()(layer_a)
layer_c = Dense()(layer_b)
model = Model(inputs=[input_layer], outputs=[layer_c])
Dude I'm not expecting you to
or something, it's been like 2 years
so the __call__ of all layers is overloaded to return a new layer connected to self
this would create input_layer -> layer_a -> layer_b -> layer_c
__call__ has two meanings:
-
Execute or cause some code to run knowingly (functions)
-
Create, construct or initialize something (classes)
I think that seems fine considering it builds tree-like syntax? ```python
tree = Nodes(
One(
MyOwnNode(),
OtherLeaf(),
),
Two(
Ooooo(),
Huhhhhhh(),
),
)
This makes sense to me when you think about how back propagation works
I would think of construction as simply calling a function tbh
The argument you made were "These smart people who made the decision had this intention, which goes against your opinion, so you should change your opinion".
I don't have any problem with that - I don't have any particular expectations for what function-call syntax does... I guess I have fewer expectations for what __call__ can do
__call__ is generally a bad idea to use from my experience
i almost never have really had to use it, prob never
No I'm simply sharing the sections of the pep that are relevant and that you guys might find interesting
I use it for decorators, but not much else.
fair enough
I actually do really find the [] idea interesting; I hadn't seen that one.
so the thing about / in paths
it's just not a common "symbol" as + or / or []
indeed / is normally "division", but it's also the separator...so I could see it as being like a DSL
i.e. "not as bad" as <<
or >>
it's also "the" separator on some OS's
Oh you found the perfect phrase ... Domain specific language
^ personally, I think "weird overloading" is justified in some cases
Because it looks cool? lol
until you hit Scala's symbol soup
str.mod for example, I believe that was talked about here tho
you literally have things like @<% or something
That isn't justified - I hate it.
me too
is there really any reason apart from history?
it's so WEIRD
C
which would fall under "history"
why was __matmul__ even added
Well, because of intuitiveness. With paths for example, / is usually the separator in some cases so people might use it as a separator.
It's specific to what's being used
couldn't numpy use something that already existed, like oh i don't know __mul__
no
does anything in vanilla py even use matmul
Bc numpy fucked up by making * element wise multiplication
as someone on here said, @ should be some sort of function composition
no
makes sense
there's a need for both
so they could do ** then maybe
and making * matrix multiplication
matrix powers aren't a thing afaik
would probably be weirder
because +, -, / would all work elementwise
and not *?
I think it has a similar meaning to Pathlib's / - since all format characters were %s "it made sense" that the operator was % values.
that's element exponentiation
There absolutely is
alright lol
and they do, but there a function for that, not an operator
unfortunately, they are 😔
although that is a bit of a math special case
compared to the more "basic" elementwise ops
So in matlab matrix multiplication is * numpy is supposed to be the matlab killer
now THAT is weird to me
how do they do elementwise?
How many ASCII symbols do we even have left to use for new operators? I can only thin of $ and !
.*
well, % used to be the way of formatting strings. It predates all of the others, and is analogous to printf-style format strings in C, which use a % prefix in front of placeholders. It's pretty weird to map that to the % operator, but that formatting did need to be able to accept a tuple, which means the choice is either using some operator for it, or making it a method like "%s, %s!".format("Hello", "world") - I'm guessing the latter was rejected because of its verbosity, and because of how common an operation string formatting is.
Do you ever see / of two variables and guess "Ah, those are two paths"?
Oh and ?
Matlab follows matrix syntax
one problem, I guess, is dynamic typing
Usually you read / and think division - that's your intuition doing that
but to be 100% honest, I was playing with a Scala library that had stuff to do with URLs
and I literally tried "/" to concatenate two fragments
\ for lambdas!
and it worked
I use it like either or in english like one/two meaning one or two
although I can see the force behind +
yup, I've heard of that
so basically history, no?
yeah
Didn't they ban images or it bc u guys have higher ranks
blue you gotta chill with these reaction media, they're huge on mobile
` could be resurrected
Thank you
history, yes - but also, it was a logical choice at the time
$ isn't used, right
yeah, I don't deny that it was
Ah, I didn't consider that
didn't they claim some operators for the future? or was that rust
Why do you say "resurrected"?
also an example, say you have two classes:
EmailName and EmailSuffix
If you want to join them together to do something like
{EmailName}@{email suffix}.com
You could use __matmul__. Addition/concatenation works fine too, but it adds a "special effect", if you implement both is fine
(I think it's called the "domain")
Well, if I scroll up I'm going to see the definition of those variables somewhere
is it strictly the domain after the @? The email address standard is quite complex
That is true, but you see the point I am making? / is read as division
Idk tbh, mostly just an example
yup, I remember reading that you can't use regexes to validate emails
I know, I was just giving some context about what led to it
Oh yeah, I definitely see the point you're making. I just think that there should be some leeway with it
but isn't the portion after the @ called the "domain"?
yup, I appreciate it
can't as in it's not a regular language hence regex is insufficient?
because it was an operator in Python 2 - you could do py a = `b` instead of ```py
a = repr(b)
` was a circumfix operator
A what
goes around the operand
I believe it is not
but I could be wrong about this
As in prefix, postfix, infix, circumfix
New word learned
I think I remember reading that it's not regular
TIL
or if not, just that it's really complex (in terms of the regex)
I've only heard of prefix postfix and infix so far
are there any postfix operators in Python?
however, a quick search suggests that I am wrong about this
(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])
very good and cool. :)
nothing comes to mind
which is pretty readable
? should have been😭
that's not completely correct, it accepts some invalid email addresses I believe
just as monads can be understood simply as monoids in the category of endofunctors
🥴
or maybe rejects some valid ones?
PEP 505 being deferred 😔
beats me, I got it from SO
I really really really wanted none awarenoperators
I'll take your word for it
I'm so disappointed
Nonecoalescing
Was that the one with the ??
Affirmative
Yep
re: pathlib using / for joining paths - if i didnt know about it, i would intuitively expect + instead of /, but after looking it up i wouldn't have trouble understanding why it is the way it is (personally i still prefer +)
re: matrix dot product vs elementwise multiplication - i really like how julia (and apparently matlab too) does it, where A op B, like A * B means an operation on the container(?) as a whole, while A .op B, like A .* B means an elementwise operation
I felt like that pep would take away a lot of readability that people love about python
WTF
(to the second part)
At this point !, ? and {} feel so un-pythonic I wouldn't like to ever see them added
I disagree to be honest. There's a lot of boilerplate with dealing with None rn
mfw im a dict
Ah shit you right
It's annoying as a heck, I agree
Tough balance
we're literally @ Java level right now
One of my main grudges with Python rn tbh is the import system, mostly because its behind the scenes machinery confuses me, but I'm slowly learning
would be nice to have it like TS, Kotlin (and I think C#?)
well, not grudges, more like things I find confusing
Idk what u r saying
I've never written java
I like the place None is at, it's not implicit like NULL but not an overly huge deal unless you have a lot of optional attributes (which isn't very pythonic in the first place).
Finders, loaders, spec loaders?, meta path, etc. etc.
Don't like all programming languages have these? They aren't open to users though..
no
Python's is massively more complex than any other language I'm familiar with.
part of Python's trend to be dynamic in a way that allows nearly everything to be customized
i find it quite intuitive
maybe i was just not that used to numpy, but i found it very hard to guess which operation was going to be elementwise and which was going to be on the array as a whole
could you give specific examples of operations which you found confusing?
I honestly can't think of many which would give rise to such ambiguity
Hadamard product vs matrix product is the only one
matrix multiplication is one of them, obviously
there's no operator for matrix exponentiation, you have to call a function
that tripped me up because i was naively expecting (A ** n) @ v to apply A on v, n times
From linear algebra if I multiply 2 matrices I would expect that to be matrix multiplication not hadamard product (element wise)
Beat me to it
okay, this is fair
no such ambiguity if you have special syntax for elementwise, A .^ n vs A ^ n
that seems quite consistent, at least
okay, I suppose this could have other applications
e.g. .+ could be "concatenate"?
That one doesn't really exist in matlab
yes but it could be
It does but it's no different
for vectors its mostly redundant since + and - on the whole vector is the same as elementwise + or -
still, i think it improves readability by better expressing what you're trying to do
seems quite specialcased for numeric computing though?
julia as a whole is quite specialcased for numeric computing :P
[a b] is horizontal, [a; b] is vertical and theres a separate method for cat along other dimensions (though i think the newest release you can use n semicolons to represent a cat along the nth dimension)
Guys i need help, its related to flask and ctypes basically. I have a client,server and a hardware.py
Now basically i scan my fingerprint, generate a ISOTemplate from the fingerprint, then match the iso templates and it gives a score. Previously i had it all in one file and, day before i separated it to server and client.
So heres my problem, Everyything works fine if i get the ISOTEmplates directly on server (they are basically stringbuffers) and compare them. But the problem is when i get the stringbuffers on client, and attempt to send it via requests to my server and then do the matching, the matching doesnt work. Something breaks with the ctypes IDK what. Id really appreciate any help. BTW, this is asked on the #help-cake channel, just a little above, please check it out as ive uploaded a bunnnch of screenshots of all the shit ive tried
this is the output for the code above
@neat basalt thanks for responding btw 😄
C++? 🙂
C++ Imports are just the C preprocessor before cpp20, and for all it's faults, its really simple
Python's insanity in terms of imports is unique, most languages just use file paths, maybe with some extra roots.
oh, I didn't realize people were talking about imports specifically
yeah, it's a bit cursed
But it also allows for things like pythonnet that just import System.Collections.Generic
And that's insane
yeah, perhaps Python went top far on the "everything is a virtual call" thing
@scarlet goblet Can you stop spamming your articles in completely unrelated channels?
You need to use backticks (`), not apostrophes (')
Those are usually located on the same key as tilda (~)
my mind is somewhat blown by this
yeah that's cool
obvious, but cool
saw a method that returned a call of an awaitable function, essentially being an awaitable itself
How exactly doesmemoryview work? I get that it's supposed to avoid copying data, but don't you have to copy data to get it out of the memoryview? Slicing a memoryview just creates a new one. Suppose:
>>> mv = memoryview(b"abcdef")
>>> mv[0]
97
Does slicing copy the first byte (b"a") here?
>>> mv[1:3]
<memory at 0x000001DEFF21D900>
>>> bytes(_)
b'bc'
To be able to work with a slice of a memoryview, you have to convert it to a bytes object. I'd imagine this copies data out of the memoryview. If so, how and why is memoryview still useful regardless? The only use case I could come up with is when you repeatedly slice a bytes/bytearray object and end up not using every slice afterwards.
You can slice a memoryview without copying data: https://stackoverflow.com/a/34257357/1016216
Yes, but what do I do with the slice afterwards? I still need to copy data to get it out of the memoryview, right?
or you could just keep your memoryview slice and reference data in that
Not really?
Say you want to compare the first two bytes, there's no need to copy the data there - just compare it with the memoryview slice.
I take it that you don't have to copy data out of the memoryview if you're able to use it in a way that leverages the buffer protocol then. That's probably the whole point of it, I get it now.
When working with binary data, which one is preferable? The linked SO answer mentions struct being able to accept memoryviews
struct.unpack_from("<I", buffer, offset)
mv = memoryview(buffer)
struct.unpack("<I", mv[offset:offset+5])
they're essentially equivalent. Both keep the entire buffer alive, both require the offset to be tracked.
@frosty panther
what's a good formatting provider which follows pep8 and is better than black
What does "better than black" mean, whats wrong with black, also #black-formatter
black but with colored diff and default settings for line length prior to wrap
Black's just about as good as it gets right now. Unless you have some specific ask and don't mind the compromises. Aka better how? Are you willing to give up deterministic behaviour for example?
no i mean a formatter which likes, you know, follows the most or all pep8 rules
black
autopep8 or yapf might work better for you but other than that I'm out of suggestions
hmm lemme try yapf
weird why does yapf not change anything, did black just do that already and yapf also wants to do the same?
interesting
whoulda thunk
Black says in its readme that it's PEP8 compliant, so I wouldn't expect another PEP8 linter to change the formatting significantly
nah, yapf defintely changes stuff
better than black
there's no suggestions
Why the importance of being pep8 compliant, the point of a formatter is to be consistent across a team, not exactly to follow some arbitrary rules
PEP8 compliant means it's consistent across Python
ironically, being 100% compliant with PEP 8 for the sake of it violates PEP 8
I think the most common deviation is line length
How so? Is there a specific section? Do you mean the "A Foolish Consistency is the Hobgoblin of Little Minds" part?
!pep 8
yep
So I just used pattern matching for first time. Used to it navigate through decoded json. I really like it, especially the ability to nest patterns but I was left a little short of completely happy due to the way sequence patterns work. It would be cool if you could match if any item in a sequence matches a pattern or if all items in the sequence match a pattern
I was trying to do something like
match foo:
case [*_, {'a' : str () as bar}, *_] :
...
Thinking it would find for me a dictionary with key 'a' whose value is a str and capture it
But we can't ignore position
I wish they would add something for this
Yeah I've encountered the same longing too
!e
def hi():
from math import *
``` what's the reasoning behind this erroring?
@feral cedar :x: Your eval job has completed with return code 1.
001 | File "<string>", line 1
002 | SyntaxError: import * only allowed at module level
because local names inside a function scope are found at compile time and import * dynamically makes names
the only way it would work is if it made them globals
huh
like py def hi(): from module import foo would work, because the name foo (and the number of items being imported) can be determined at compile time
right
yeah, local names are determined at compile time, and instead of a string-PyObject* dict, like globals, they're stored in an array and referenced to via index. This makes them faster and that's why you can get a "Variable referenced before initialization" in a function, but not in a module
No wonder why the bytecode op name is "LOAD_FAST"
In Python 2 this did work, silently downgrading the whole function to non-fast locals - using LOAD_NAME like in module/class scope, and using a dict.
interesting i wonder why that got dropped for python 3
I'd assume it has something to do with consistency and reflection
If updating the value returned by locals() sometimes updates locals and sometimes doesn't, it's probably not fun to debug
Indeed, silently changing the performance like that is problematic.
You can change the dict returned by locals(), but you still can't use the variable normally afterwards.
!e
def f():
locals()["x"] = 42
print(locals()["x"])
print(x)
f()
@quick snow :x: Your eval job has completed with return code 1.
001 | 42
002 | Traceback (most recent call last):
003 | File "<string>", line 5, in <module>
004 | File "<string>", line 4, in f
005 | NameError: name 'x' is not defined
!e isn't there some funny business like that that you can do
def f():
locals = lambda *_: {"x": 42}
print(locals()["x"])
print(x)
f()```
@unkempt rock :x: Your eval job has completed with return code 1.
001 | 42
002 | Traceback (most recent call last):
003 | File "<string>", line 5, in <module>
004 | File "<string>", line 4, in f
005 | NameError: name 'x' is not defined
weird
Well that part doesn't surprise me, assigning to the name locals doesn't do anything special
Discovered this very intriguing "anomaly" or pattern if you will from a help channel here.
!e
sets = [set(range(i, i + 3)) for i in range(0, 21, 3)]
for s in sets:
print(s)
@unkempt rock :white_check_mark: Your eval job has completed with return code 0.
001 | {0, 1, 2}
002 | {3, 4, 5}
003 | {8, 6, 7}
004 | {9, 10, 11}
005 | {12, 13, 14}
006 | {16, 17, 15}
007 | {18, 19, 20}
How come every third element is out of order
So, sets don't preserve order and it makes sense that some are unordered
But then how come that every 2 out of 3 are ordered
sets are unordered, and any order that happens to be displayed is not to be relied on
probably just a happy coincidence on the internal algorithms used by sets
This is so weird but also cool. I wanna understand why
!e
sets = [set(range(i, i + 10)) for i in range(0, 100, 10)]
for s in sets:
print(s)
@unkempt rock :white_check_mark: Your eval job has completed with return code 0.
001 | {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
002 | {10, 11, 12, 13, 14, 15, 16, 17, 18, 19}
003 | {20, 21, 22, 23, 24, 25, 26, 27, 28, 29}
004 | {32, 33, 34, 35, 36, 37, 38, 39, 30, 31}
005 | {40, 41, 42, 43, 44, 45, 46, 47, 48, 49}
006 | {50, 51, 52, 53, 54, 55, 56, 57, 58, 59}
007 | {64, 65, 66, 67, 68, 69, 60, 61, 62, 63}
008 | {70, 71, 72, 73, 74, 75, 76, 77, 78, 79}
009 | {80, 81, 82, 83, 84, 85, 86, 87, 88, 89}
010 | {96, 97, 98, 99, 90, 91, 92, 93, 94, 95}
!e note also:
s = set()
for i in range(20):
s.add(i)
print(s, end=" ")
@pearl river :white_check_mark: Your eval job has completed with return code 0.
{0} {0, 1} {0, 1, 2} {0, 1, 2, 3} {0, 1, 2, 3, 4} {0, 1, 2, 3, 4, 5} {0, 1, 2, 3, 4, 5, 6} {0, 1, 2, 3, 4, 5, 6, 7} {0, 1, 2, 3, 4, 5, 6, 7, 8} {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10} {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11} {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12} {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13} {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14} {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17} {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18} {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}
huh, strange, I wanted to show that the order may change when the set resizes
but looks like small ints are truly blessed
i think the hashes for small ints are just those ints
they are
!e
for i in range(10):
print(hash(i), end=" ")
@deft pagoda :white_check_mark: Your eval job has completed with return code 0.
0 1 2 3 4 5 6 7 8 9
!e
!eval [code]
Can also use: e
*Run Python code and get the results.
This command supports multiple lines of code, including code wrapped inside a formatted code block. Code can be re-evaluated by editing the original message within 10 seconds and clicking the reaction that subsequently appears.
We've done our best to make this sandboxed, but do let us know if you manage to find an issue with it!*
e
e
!e
!eval [code]
Can also use: e
*Run Python code and get the results.
This command supports multiple lines of code, including code wrapped inside a formatted code block. Code can be re-evaluated by editing the original message within 10 seconds and clicking the reaction that subsequently appears.
We've done our best to make this sandboxed, but do let us know if you manage to find an issue with it!*
!e
print("Hello this is SNIPER")
@rotund grail :white_check_mark: Your eval job has completed with return code 0.
Hello this is SNIPER
the hashes are cached so that shouldn't change order
One doesn't follow from the other, no:
{'rjyme'}
{'rjyme', 'lzikw'}
{'pfxua', 'rjyme', 'lzikw'}
{'pfxua', 'dhfno', 'rjyme', 'lzikw'}
{'dhfno', 'pfxua', 'lzikw', 'nqlka', 'rjyme'}
{'dhfno', 'pfxua', 'lzikw', 'nqlka', 'rjyme', 'ygfsn'}
{'dhfno', 'pfxua', 'lzikw', 'mhxsf', 'nqlka', 'rjyme', 'ygfsn'}
here both the absolute and the relative order changes as I add elements, when the set resizes
the position in the hash table is basically hash % n, which can and will change when n changes, unless hash<n (and that's why it works for small ints)
oh, I knew that 😔
!e
s = set()
for i in range(10):
s.add(i)
print(s)
@dusk comet :white_check_mark: Your eval job has completed with return code 0.
001 | {0}
002 | {0, 1}
003 | {0, 1, 2}
004 | {0, 1, 2, 3}
005 | {0, 1, 2, 3, 4}
006 | {0, 1, 2, 3, 4, 5}
007 | {0, 1, 2, 3, 4, 5, 6}
008 | {0, 1, 2, 3, 4, 5, 6, 7}
009 | {0, 1, 2, 3, 4, 5, 6, 7, 8}
010 | {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
!e
s = set()
for i in range(10):
s.add(str(i))
print(s)
@dusk comet :white_check_mark: Your eval job has completed with return code 0.
001 | {'0'}
002 | {'0', '1'}
003 | {'0', '1', '2'}
004 | {'0', '1', '3', '2'}
005 | {'3', '2', '0', '4', '1'}
006 | {'3', '2', '0', '5', '4', '1'}
007 | {'3', '2', '0', '5', '4', '6', '1'}
008 | {'3', '2', '0', '5', '4', '7', '6', '1'}
009 | {'3', '2', '0', '8', '5', '4', '7', '6', '1'}
010 | {'3', '2', '0', '9', '8', '5', '4', '7', '6', '1'}
!eval def f():
locals = lambda *_: {"x": 42}
print(locals()["x"])
print(x)
f()
@sick aurora :x: Your eval job has completed with return code 1.
001 | 42
002 | Traceback (most recent call last):
003 | File "<string>", line 5, in <module>
004 | File "<string>", line 4, in f
005 | NameError: name 'x' is not defined
@pearl river @deft pagoda Regarding the peculiar order of sets. I asked on the python forums and got a reply. If you guys are interested: https://python-forum.io/thread-35918-post-151362.html#pid151362
TL;DR: Hashmaps start with size 8, so the last 3 bits of the hash are relevant.
@quick snow :white_check_mark: Your eval job has completed with return code 0.
001 | {0, 1, 2} ['000', '001', '010']
002 | {3, 4, 5} ['011', '100', '101']
003 | {8, 6, 7} ['1000', '110', '111']
004 | {9, 10, 11} ['1001', '1010', '1011']
005 | {12, 13, 14} ['1100', '1101', '1110']
006 | {16, 17, 15} ['10000', '10001', '1111']
007 | {18, 19, 20} ['10010', '10011', '10100']
!e
sets = [set(range(i, i + 3)) for i in range(0, 21, 3)]
for s in sets:
print(s, [f"{x:03b}"[-3:] for x in s])
@quick snow :white_check_mark: Your eval job has completed with return code 0.
001 | {0, 1, 2} ['000', '001', '010']
002 | {3, 4, 5} ['011', '100', '101']
003 | {8, 6, 7} ['000', '110', '111']
004 | {9, 10, 11} ['001', '010', '011']
005 | {12, 13, 14} ['100', '101', '110']
006 | {16, 17, 15} ['000', '001', '111']
007 | {18, 19, 20} ['010', '011', '100']
I would like to read up on mental models people use for Python
Basically what pythontutor does, but articles explaining the mechanism behind what Python does internally
Much appreciated
Here is the result:
that has the book linked iirc
and if you want a free version that still teaches you a decent chunk of it
.rp 1 Python Internals
Here is the result:
oh huh
I can try looking it up, but thanks!
Np
is this the new #advanced-discussion ?
no, its the same advanced discussion just the name is different
ah nice
i just came across this so im probably being dumb but if stdin is actually a file and to read from it, you read the file, then does that mean user input comes with the overhead of writing to a file?
because thats a pretty expensive operation computationally
It's file like, usually not an actual file
nah just a stream
in memory?
The streams are also buffered by default so not that huge of a concern even if backed by an actual file
man what happened to PEP505
The "None-aware attribute access" operator ?. ("maybe dot") evaluates the complete expression if the left hand side evaluates to a value that is not None
this part in particular would be so useful
Yup, it would be heh
Also for type hints. int? instead of Optional[int] or int | None would be so much nicer
int | None exists now IIRC
i think swift does that
I did write just that
and most of the time, you want exceptions so that there is at least some indication of why the value isn't present
if only there was a way to hint those 😔
you can use your own error values
that's just going to be worse exceptions most of the time
like ```py
@dataclass(frozen=True)
class SimpleError:
message: text
def __bool__(self) -> Literal[False]:
return False
and then you realize that your fancy ?-syntax doesn't work for these
or for exceptions
so now you have a new syntax for something that's already possible, and it still doesn't work
yeah, comprehensive handling of partial values like rust has is really the way to go, but you can't do that in python without changing the whole language.
much like := 🙂
although to be honest, I found a few uses for :=
It's handy if you have an elif chain
raku has managed this whole Failures as expressions in a dynamic language quite well, though you rarely check for specific failures in practice.
everyone in #esoteric-python disliked that
you can just variable + variable for variable in [value] for esoteric python.
Or ParamSpec. Or the new callable proposal.
special casing None doesn't seem like the way to go to me
getting better exception handling options would IMO be better
Yeah same, my main one being truthiness checks
just use the Result monad (c) dry-python
that or any/all's
well... I guess that's what Rust does 🙂
let me pattern match on exceptions, give me except try, and a lot of the usecases for None aware operators will just go away.
also tell Python developers to put useful information in an exception instead of formatting it inside a string!!! 
indeed
I forgot, how is it that super() infers the class and instance arguments? Special parsing, or meta-programming?
I believe special parsing, though you could probably do it with metaprogramming as well.
With metaprogramming, like with inspect? I'm not sure how you could get that information within super.__init__
If it's going to be a blessed special case anyway, I don't think the parentheses should be necessary.
class super:
def __init__(self, cls, inst):
self.cls = cls
self.inst = inst
def __getattr__(self, attr):
for cls in self.cls.__mro__[1:]:
try:
return getattr(cls, attr)
except AttributeError:
continue
raise AttributeError
I assume a pure Python implementation of super would look like this.
it would be cls.__getattribute__(self, attr) I am pretty sure
I don't remember the difference between __getattr__ and __getattribute__
__getattr__ is the fallback
it allows you to customise things without special casing things like __class__
as well as whatever "real" attributes and method you may have
super is still a normal callable 🤷 so e.g. changing super.__name__ to mean super().__name__ would be a breaking change
I see your point about it being a breaking change. This is just my "if we could do it over again" wish-list.
super is a normal-ish class, and you're constructing an instance of it when you call super()
That sounds so wrong for some reason
Is it implemented as some kind of mega wrapper descriptor or something
the old-school way of constructing an instance of super was super(StaticClass, self), and what that does is make it so that any attribute lookup on the super() instance returns the corresponding attribute from the first class after StaticClass in the MRO of self
Hmmmmmmm could you reimplement supper in Python
the new-school way is just automatic shorthand for that old-school way - super() expands to super(__class__, <firstarg>) where __class__ is a built-in variable that points to the class a method is statically defined in, and <firstarg> is the first argument to the method that super was called in.
I feel like it should be possible
I think it would be, yes
Talk idea? PauseChamp
could be a good talk. Lots of people find super() confusing, and showing Python code that does the same thing might be illuminating
The way it works is that if super is referenced in any method of a class, it creates a __class__ closure cell, and adds that into the function. super() then uses the C equivalent of sys._getframe() to fetch that and also the first parameter.
i always thought super().__init__(avar=avalue) made a self.<var> except in one line
just for fun: ```python
import functools
from typing import *
E = TypeVar('E', bound = Exception)
class Error(Generic[E]):
_exc: E
match_args = ('_exc',)
def __init__(self, exc: E):
self._exc = exc
def __eq__(self, other: Any) -> bool:
if isinstance(other, __class__):
other = other._exc
return (
isinstance(other, Exception)
and (self._exc.__class__ == other.__class__)
and (self._exc.args == other.args)
)
T = TypeVar('T')
R = TypeVar('R', T, Error)
P = ParamSpec('P')
def with_error_match(func: Callable[P, T]) -> Callable[P, R]:
@functools.wraps(func)
def inner(*args: P.args, **kwargs: P.kwargs) -> R:
try:
return func(*args, **kwargs)
except Exception as exc:
return Error(exc)
return inner
>>> def itemgetter(key: Any) -> Callable[P, R]:
... @with_error_match
... def itemgetter(value: Sequence[T] | Mapping[Any, T]) -> T:
... nonlocal key
... return value[key]
... return itemgetter
...
>>> def test_itemgetter(value, /, key):
... f = itemgetter(key)
... match f(value):
... case int(item):
... print(f'success: {value}[{key!r}] -> {item}')
... case Error(exc):
... match exc:
... case IndexError() | KeyError():
... print(f'failure: {value}[{key!r}]')
... case _:
... raise exc
...
>>> test_itemgetter([1, 2, 3], key=1)
success: [1, 2, 3][1] -> 2
>>> test_itemgetter([], key=0)
failure: [][0]
>>> test_itemgetter(dict(foo=1, bar=2), key='bar')
success: {'foo': 1, 'bar': 2}['bar'] -> 2
>>> test_itemgetter(dict(), key='foo')
failure: {}['foo']
>>> test_itemgetter(42, key=0)
Traceback (most recent call last):
...
TypeError: 'int' object is not subscriptable
I kind of disagree, I hate how JavaScript does this and much prefer how self in Python works. Maybe it could've been __super__ instead then but just naming it super as a normal variable I'd hate.
Before I knew about the specialcased super(), I would constantly make the same mistake of explicitly passing self into the method, i.e. ```py
super().init(self, arg1, arg2, ...)
IMO that would be the better option, as I'm not a big fan of specialcasing, but I could see this being not very static/classmethod-friendly
huh, I'm not sure if python R = TypeVar("R", T, Error) is valid type-checking-wise, especially in something like mypy where T would be declared as unbound
and there isn't something like Error[E] in that annotation
AnyException = BaseException
Result = T | Error[E]
AnyResult = Result[T, AnyException]``` perhaps something like this, and then `T` gets mapped to `AnyResult[T]`
I really liked the idea of wrapping exceptions into the Error type ngl
haha, someone caught me on begin rust-y again

something like ResultBase with all the methods seems nice, and then Ok (or Value) and Error can derive from it
though, full ADTs could be super neat for that
What's special-cased, isn't super() technically an instance of the parent.
Instead of raising them O_o
I've actually implemented something like that once
specifically, I was doing a lot of similar tasks in worker threads, which may often result in exceptions
and I'd catch all exceptions and wrap them into an Error class with the exception inside. That was, the threads always finish without crashing, which was nicer to handle.
indeed
Zero-arg form of super() is special-cased and transformed into the old super(Type, self)
I'd imagine something like ```python
T = TypeVar("T")
E = TypeVar("E", bound=AnyException)
@enum
class Result(Generic[T, E]):
Ok: Case[T]
Error: Case[E]
def unwrap(self) -> T:
match self:
case Ok(value):
return value
case Error(_):
... # handling
Ok = Result.Ok
Error = Result.Error```
Yes but you claim that the fact that you don't pass self after that (to __init__() was the example you gave) is special cases.
No, I'm claiming that instead of having a specialcase, making it an actual zero-arg call and explicitly passing self to let it figure out the MRO from the instance passed would be a better solution, IMO
Although, now that I think about it, it's probably not going to work the way I want it to work, as there wouldn't be a way to tell what context the call is in
Simply looking at the type would never go up the mro unless there's no overload on this type, in which case super wouldn't be used, hmm
right, you need self and class to be explicit.
Having them filled in implicitly just doesn't sit right with me. Thinking about it more, I think I would prefer something like a __super__ or __base__ attribute on an instance with a pre-instantiated super() proxy, though I do understand why not a lot of people would like that
It's not a proposal by any means, just heavily biased hindsight
how could you have a pre-instantiated super() proxy? The proxy depends on which class you are using it from.
Right, so the same issue applies, hrmm
Actually, given that the called methods operate on the proxy, it could return another proxy on __super__ access that would be a level higher. I'd have to play around with it a bunch
Descriptors?
Descriptors get a type and an instance
The type wouldn't change if the instance passed is the same
you'd always get the deepest type available
f
hi
evryone
i have a question
i am new here
i am tryng to go in some room here
where i can tolk
but i din t find any rooms to do it
there reason is bicouse anyone speaks
or it my fault
bicouse i am still beginne on discord
and i can t find it
?
@hidden bloom if you are looking for help you should have a read of #❓|how-to-get-help 🍏
hmm, so __class_getitem__ exists, but what about __class_instancecheck__? I've seen a lot of people have to make metaclasses to implement an instance check on the class level
and same goes for __class_subclasshook__
I get why __class_getitem__ was added because of its usage with typing.. but I don't think we should add dunders for all uses of metaclasses
I've seen instancecheck be quite a common one though implemented onto metaclasses
Agreed. Since isinstance operates on the type of an instance, it makes sense for that to stay on the metaclass.
TFW you need to add optimizations to a python program because 56 seconds is too long
So can someone explain to me. We cannot pattern match on type?
Or you can but you must call the type
You have to call the type, yeah
However this doesn't work on callable
Right
But
isinstance(lambda x: x, callable) returns true
No it doesn't
So we can't check if something is callable then
callable is a function IIRC
That is basically equivalent to hasattr(..., '__call__')
(I think)
You could use a guard
That's lame though
case x if callable(x)
I'm not sure what's used to check types for pm, but there's probably a way to make a class that will pretend to be the type of any callable
You do
Type(kwargs) to see if it's an instance of Type and then if each attribute in kwargs has the subpattern
Id probably have to make my own type and override __isinstance__
Yeah
Seems less than ideal
typing.Callable runs isinstance checks but doesn't work for pm
>>> from typing import Callable
>>> isinstance(lambda: None, Callable)
True
>>> match lambda: None:
... case Callable():
... print('Yes')
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
TypeError: called match pattern must be a type```
This seems like a major short coming
Not really
well, the thing is
the only way to really check if something is callable is to call it
Yeah
There's no base class that all functions and methods inherit from?
!e
No, it's just a magic method
class Foo:
def __call__(self):
return 42
print(Foo()())
@grave jolt :white_check_mark: Your eval job has completed with return code 0.
42
If you have a __call__ method, you can be called
What about types.FunctionType or LambdaType`
There's no separate LambdaType
LambdaType is the same as FunctionType
still, a callable object doesn't have to inherit from FunctionType
I'm pretty sure FunctionType is an invalid parent
!e ```py
class Test(type(lambda: None)): pass
@spice pecan :x: Your eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 1, in <module>
003 | TypeError: type 'function' is not an acceptable base type
I don't like this
python being duck-typed?
So I have to either propogate another attribute on the class whose instance state I am trying to match on or do a guard
what do you want to do?
You can probably use a real type with an isinstance hook
There's just some things about pattern matching that are really lack luster
Pattern matching does feel half-baked
Like I should just able to check it an attribute is callable
It should get a special hook or something being a built in
There is
what do you mean by "X is callable"?
It's named callable and you can run a guard to check it :)
If you want to just do case SomeCallableType(): you could use a real type with an isinstance hook
Either handmade or collections.abc.Callable
!e ```py
from collections.abc import Callable
match print:
case Callable():
print('yes')
@spice pecan :white_check_mark: Your eval job has completed with return code 0.
yes
Is foo callable?
class Foo:
def __call__(self, *args, **kwargs):
raise RuntimError(":^)")
foo = Foo()
if so, then you can use callable (or collections.abc.Callable)
Oh
Well there you go thank you
I think you meant to thank GKK
I find that much better than doing the guard
Thank you
The other issue I had with pattern matching is related to sequences
I feel like I should be able to match on if any (or all) of the entries in a sequence match some subpattern
So say I'm parsing json and I want to find in a list an object (aka dict) that has this pattern {"a"=str(), "b"=5}
I feel like that should be possible, right now we have to know absolute position of everything which is cumbersome for matching anything other than the first couple or first last
hmm, so something like case x if "a" in x and "b" in x and isinstance(x["a"],str) and isinstance(x["b"], int):?
I wonder if jmespath can match on a condition like that
Hmm, just after this discussion about matching exceptions, there is now a thread on matching exceptions in the python mailing list
somewhere up here I remember someone talking about matching excpetions
with pattern matching ?
like this?
def match_exception(e: Exception) -> None:
match e:
case FileExistsError():
print("Does this matter?")
case TypeError():
print("It's a TypeError.")
case Exception():
print("Some other error.")
case _:
raise TypeError(f"Error: {e} is not an error!!!!")
How could I check if an attribute in class dictionary from metaclass is a function/callable? I'm trying to decorate all methods in that class, but I need to distinguish the methods from other things ```py
class FooMeta(type):
def new(mcls, name, bases, clsdict):
for name, val in clsdict.items():
# check if val is a method
you only want the methods/functions defined on that class?
yeah
I also need to exclude static and classmethods, though I think I can just do that with isinstance(val, staticmethod), but I've no idea how would I check if something is a function directly
i believe you can do it
I just don't know how would I get the methods (including properties)
I'm pretty sure it's doable I think I've seen someone do it on a talk, but I forgot how it was done
oh, apparently, there's callable builtin function, how did I miss that
you can use __objclass__ for at least some members
so I guess I can just do this```py
for name, val in clsdict.items():
if callable(val) and not isinstance(val, (staticmethod, classmethod)):
...
that will bring in stuff from object i think
yeah, but that's fine there's a filter for only specified methods later, I just needed to check that those specified methods are callable
though this probably won't catch property but I think isinstance(val, property) will handle that one and there should be some variable under the property class storing the actual function so I can decorate it
!e this kind of works
from inspect import getmembers
from types import WrapperDescriptorType
from typing import Any
def get_own_members(cls: type) -> dict[str, Any]:
return {
k:v for k,v in getmembers(cls)
if not isinstance(v, WrapperDescriptorType)
and getattr(v, "__objclass__", None) == cls
}
print(get_own_members(BaseException))
@lusty scroll :white_check_mark: Your eval job has completed with return code 0.
{'__cause__': <attribute '__cause__' of 'BaseException' objects>, '__context__': <attribute '__context__' of 'BaseException' objects>, '__reduce__': <method '__reduce__' of 'BaseException' objects>, '__setstate__': <method '__setstate__' of 'BaseException' objects>, '__suppress_context__': <member '__suppress_context__' of 'BaseException' objects>, '__traceback__': <attribute '__traceback__' of 'BaseException' objects>, 'args': <attribute 'args' of 'BaseException' objects>, 'with_traceback': <method 'with_traceback' of 'BaseException' objects>}
problem is that I haven't actually made the class object yet, I only have the class dict
oh really? then you can use that (e.g. ns.items()) instead of getmembers(...), I think
wait, that won't work
what is in the dict?
it's the dict from the metaclass
won't it just be the things you're interested in, then? and you could just check if they're callable or have a __get__ attribute, maybe ?
!e ```py
class FooMeta(type):
def new(mcls, name, bases, clsdict):
print(clsdict)
clsobj = super().new(mcls, name, bases, clsdict)
return clsobj
class Foo(metaclass=FooMeta):
def my_method(self, x):
print(x)
@staticmethod
def static_m(x):
print(x)
@property
def my_prop(self):
return 1
@deep bramble :white_check_mark: Your eval job has completed with return code 0.
{'__module__': '__main__', '__qualname__': 'Foo', 'my_method': <function Foo.my_method at 0x7fd832b98ca0>, 'static_m': <staticmethod(<function Foo.static_m at 0x7fd832b98d30>)>, 'my_prop': <property object at 0x7fd832b97d80>}
ah, ok
not quite, classes can have their own variables, which is why I needed to only get the callables
like __module__
yeah but it can also be user-defined ones ```py
class Foo:
X = 5
well, I'm overriding some of the things in that dict before initializing the class
I could probably do it with the class object too but I've already built everything to work with the dict, not the class object
what about using __prepare__ ?
how would that help me, I don't need to adjust the initial dict, I need to adjust the values in that dict, but to do that I need those values there. __prepare__ would allow me to add some initial values to the dict, or change it's type to do something special but that's not what I need
it would help to see what kind of adjustments you're making I think
it's fine, this basically did the trick, if there's still something unclear I'll ask again, for now what I wanted was solved with this
ok, cool
whats the logic for checking if you can assign __class__ to a type?
class Asset: ...
class Item(Asset): ...
class BaseBackpackItem(Item): ...
class BackpackItem(BaseBackpackItem): ...
class Casket(BackpackItem): ...
```i have a class hierarchy like this (they all have slots), and i want to make a BackpackItem an instance of Casket but when i try and do that i get this error
`TypeError: __class__ assignment: 'Casket' object layout differs from 'BackpackItem'`
Nah, more like if you catch an error you can pattern match it with Exception, but there's a thread on the mailing list to talk about changing that
You want to change the class of an instance of BackpackItem, or of BackpackItem itself?
In any case: for this to work, both original and new class need to be:
- both with or both without slots
- user-defined and not deriving from any C-classes (e.g. str, list, ...)
- if they have slots, the slots need to be compatible: same size, same size of elements, ...
And if you really meant changing the class of the class: won't work, because also both classes need to have a common base (i.e. object or type)
im not changing the type im changing an instance
Casket has an extra slot so id guess that would be the issue?
Changing the class of an instance is a very strange thing to do. I'd avoid that if I were you.
also, slots aren't necessary, so you could drop that too.
i dont think its easy to avoid, i want to have a nice api where youre backpack contains backpack items and caskets, caskets are items but have their own storage for more items
why does it start as an instance of BackpackItem, and then changes to Casket? Why doesn't it start as Casket?
When I fetch the backpack the item starts off as a BackpackItem as I can't tell when it's fetched if it should be a Casket
fetched from where? how do you fetch it? And what then determines that it should be a casket?
It's fetched from steam using the api and the def index of the item determines if it's a Casket
why not wait until you have that index before making the object?
even if you do change the class of an instance, you won't get the initialization of the extra attribute a Casket has. It just gets really fiddly to try to change the class.
Yeah I add the extra attribute myself
I mainly just thought changing the class would be good so I could separate out the functionality for adding/removing items to the casket into a separate type safe place
i would strongly advice not changing the class of an instance. it sounds like the opposite of type-safe.
Well it's not type safe for me but it is for the end user
i don't understand how this makes it type safe. The user only sees the Casket object, right? Does it matter how it got created?
Yeah they only see Casket and not the horrific things I had to do to make it
ok, so if you make it a different way, it will still be type-safe.
and don't use slots, they are rarely needed, and are a headache.
why are slots a headache, out of curiosity? I admit I've rarely used them but they seem nice
something I was hoping to mess around with in the future
first, you have to repeat yourself to declare them and then initialize them. and they are what's getting in the way of the thing @steel solstice wants to do (though I think they shouldn't do it).
before using slots, you should at least clarify why you want to use them.
can't you just set slots=True on a dataclass
or attrs
that's the way in which I was planning to use them
(i realized as i was typing that dataclass/attrs probably made it simple)
ah
yeah, to me it seems like very little downside, moderate benefit
it does make it a runtime error to add attributes, iirc
which from my perspective is generally a good thing
i hope your callers weren't hoping to do that 🙂
I am my caller 🙂
this is internal code in other words
but there are some tricky cases involving dataclass inheritance that I'm not sure if slots will behave properly or not
In particular, I have a dataclass that overrides the type of a base dataclass and I'm pretty sure this isn't handled correctly
it overrides the type of one of the members, I mean
iirc __slots__ has some confusing behaviour with inheritance too
inheritance with dataclasses is kind of a fraught issue to be fair so I can't blame it too hard
i can understand why it's often not supported (though it doesn't prevent it from being annoying)
Not really, you just inherit your parent's __slots__
it has some more edge cases, like with multiple inheritance only one of the base classes is allowed to have a non-empty __slots__
I'm only using them cause I want them to be immutable
And yeah the thing hsb is describing is also a pain
sometimes in Python it doesn't pay to be overly defensive.
(also, do slots make it immutable? It just makes it so you can't add ad-hoc attributes)
Why does the user creating new attrs on the object matter to you?
I'd really only use slots if I had some object that has a very high number of instances, but then it might be better to go out of python
I'm also not sure why you want to prevent people making new attributes. what bad thing would happen if they did?
and it's their program, so why should you get a vote?
I do also have a lot of instances of things (a user can have multiple inventories and they can each have 5000 items each)
Then slots makes sense
eh, 5000 isn't so many
That's gonna depend on what those instances themselves are storing no?
Depends on how much memory you have available
Many people host Python scripts on extremely limited VMs
Some of those 5000 items can also contain other items, or 5000 in total?
They could but for the purposes of this conversation no that's the max
it makes sense to use slots if you are running out of memory, true.
@steel solstice immutable would mean you couldn't assign a new value to an existing attribute
yeah they are meant to be immutable then but enforcing that hurts performance
does it? I hadn't heard that before.
well id have to implement a __setattr__ and it wouldnt be infallible you could use object.__setattr__ (id have to use this to set my own attributes in init)
yes, I see what you mean.
well, it's your library type right?
yes, but it's not your program? Why not let the caller do what they want?
because, you want to know exactly what's going on with your type, so that when your library operates on an instance of your type, there aren't surprises
the whole notion of contracts which underlies, implicitly or explicitly, most of software engineering, works in a certain way
You can do XYZ, and when you do those things, I promise to do ABC
There's nothing vaguely unreasonable about constraining the scope of what users can do. To be honest, seems more like rationalization for python's lack of features in that area.
To be clear; not having said features isn't necessarily the end of the world either, and there's always a lot of care required in deciding what usages are appropriate. But to blanket say it's always wrong seems extreme.
you make a good point, but it can also be useful to be able to annotate an object for your own purposes.
Sure; that's precisely why non-intrusive approaches are preferred, and why non-intrusivity is such a highly discussed property of approaches to certain kinds of problems
i didn't blanket-say it was always wrong. I asked what bad thing would happen
Exactly one of the issues with inheritance btw
Sorry, it seemed that you were very definitely implying such but perhaps that is just my interpretation 🙂
to be fair, i was on the "always wrong" side, but I try to ask about things
indeed, that was the impression I had. I think there's a time and a place for locking things down and you can easily go too far. But not allowing the addition of properties to a class isn't really going far, IMHO
it's a very basic sort of guarantee
but it would only hurt if your code did "for all attributes" somewhere, wouldn't it?
well, at the very least it could hurt for other forms of introspection as well
but yeah, that's the obvious way in which adding attributes could break
but that's a valid technique obviously. And in larger projects, you can very easily have one person coding up some concrete dataclasses A, B, C, etc while another person is writing generic/introspective code that is supposed to operate on entire types of dataclasses for whatever purpose. that happens even inside my small team.
I'll grant you there are failures that could happen from unwanted attributes being added.
The flipside is, when would you ever look at somebody adding ad-hoc attributes to a dataclass and think it's a good idea
I'm pretty sure I've never, ever, ever thought that's a good solution to any problem
In the age of mypy it's a worse idea than ever; mypy will also flag every single access to your ad hoc attribute as well, you're losing the protections of the type system in checking your work because some instances of that type will have the extra type, some will not
just create a new type that holds the library type and any new attributes (or even derived from it)
and (to add to your argument): there are other ways to annotate objects if you need to.
wait: deriving from it won't help: my new attributes would be on the object, and the base class would still stumble over them.
right, so it depends exactly how you do the inheritance. but at the very least, for example, the library can check the precise type
if you inherit from a dataclass into another dataclass, then you can use the schema of the overall object in an organized way
But yes, inheritance can in fact also cause many problems, which is why it's become so much more common to take measured approaches to inheritance
Even in Java, the father (mother?) of overuse of inheritance, it's often (usually?) recommended that you write classes as final by default and only open them to inheritance if you're designing around it
i guess we can add it to the pile of ideas that seemed good at first, then we had to add some guardrails once we realized the dangers.
yes, indeed.
but yeah, that's why non-intrusiveness is such a great thing, lets multiple users do things with a type without bothering each other. Another place where inheritance fails pretty hard.
Not having non-intrusive dynamic polymorphism is prob one of the worst things in Kotlin for me, and it's kind of behind the times (compared to Swift, Rust, even Go)
But then in Rust you don't have non-intrusive reflection, just intrusive macros... so 🤷♂️
Thoughts on str subclasses? I sometimes feel like I over-use it
It's almost like a curse to me that it was allowed in the first place 😅
the downside of builtin subclasses is that calculated values tend to go back to the builtin rather than your class.
Representations that provide little-to-nothing on top of a string (maybe a few attributes).
Here's a particular use-case I have right now: https://github.com/wumpyproject/wumpy/blob/main/library/wumpy-models/wumpy/models/emoji.py#L36-L47
library/wumpy-models/wumpy/models/emoji.py lines 36 to 47
def __new__(cls, data: Dict[str, Any]) -> 'Reaction':
id_ = _get_as_snowflake(data, 'id')
name = data.get('name', '_')
animated = data.get('animated', False)
# If id_ is None then it is default Emoji and only consists of the
# emoji name (which is a unicode eomji).
return super().__new__(cls, name) if id_ is None else super().__new__(
cls,
'<' + ('a' if animated else '') + ':' +
name + ':' + str(id_) + '>'
)```
may be better off as an attribute then?
The reason I am asking is because I am considering adding another 😅
It's for Discord assets, because they're just a link but I need one method to read them
what does this do that a function returning a string couldn't do?
or, a simple class with a __str__ method?
It has those separated attributes, and later I subclass with more for when I have them.
The thing is that some reactions only have a name so they'd be represented as a string. I thought I'd be easier to deal with if the return value is always a string
Because an emoji is more-or-less a nicely formatted string
it sounds like a class with a __str__
Hmm, yeah that's what I had originally
I think parsing JSON data in __init__ is a bad idea - makes it harder to re-use this class in other contexts
(for example, in tests)
wait... why does Emoji subclass from Reaction?
I think I'm going to make my own wrapper 👀
also, slots again. It's slots day here in #internals-and-peps
Because Reaction is in fact an emoji (thanks Discord), just a more restricted one
What did you guys do in Ablaze then? I see now
VCO deleted the repo just an old link that didn't redirect
I disagree still, the majority of the time you will have all the data represented as the payload?
They're just wrappers over that payload, why wouldn't you initialize them with that
I don't think I understand what exactly you're modeling with that class
In Discord, a "reaction object": https://discord.com/developers/docs/resources/channel#reaction-object consists of:
- count
- "partial emoji object" (I hate you, Discord)
Integrate your service with Discord — whether it's a bot or a game or whatever your wildest imagination can come up with.
...because all attributes it adds are optional - I am not too sure at this point 😅
In your code, Emoji inherits from a Reaction.
I think it's wrong:
- emojis will still exist even if discord disables reactions
- emojis exist in context other than reactions, such as in message text
I don't think you need to expose the horrors of the Discord API to the library users. It's your job to expose a nice API that makes sense.
Ideally, the library should present an interface familiar to a user of Discord ,not the Discord API
For example, if you ask an average Discord user if a channel category is a channel, they'll say: "WTF? Are you stupid?"
And if they're found there they are strings. Emoji being a type of reaction isn't as important as the inverse - reactions are a much more restricted type of emojis.
Side note: Is this the best place to discuss this? I would like to continue, but not sure if this still applies here
let's move to #software-architecture
No, see #❓|how-to-get-help
didn't you already do this or try and decide against it smh
lol very helpful docs
after upgrading, mypy is giving me tons of "error: Library stubs not installed for pytz" even though I'm running it with --ignore-missing-imports
anybody encounter this?
I would ask in #type-hinting
alright thanks
this code example wont work
(assuming future annotations is disabled)
variable annotation in function will evaluate expensive_mod, but it isnt imported, so it is NameError
Neither of those will evaluate, as explained in the text
this msg to Lu1s, if you don't know how, then first you should clear your this doubt for sure. Go to YouTube, watch some python tutorials and see how this language works, first you will feel it's too easy then you will counter some worth tackling topics loops, fuction, classes and there are so many more. I can't tell all that in text. So, go and check out by yourself gradually your path will become clear how to learn python..
oh, really
i thought that local var annotations is evaluated at function-call time, but they arent evaluated at all
sorry
Maybe post to #discord-bots instead?
sure, sorry didnt know where to put
By the same line of thinking that supports whitespace syntax over semicolons and curly braces (which I see as an unmitigated victory for Python), I think it also follows that commas shouldn't be required, either.
It would be ambiguous in many cases, but I am inclined to agree with the idea
implicit string concatenation would have to go away. Nothing else immediately comes to mind.
while not ambiguous in terms of the parser, tuples would be less obvious to the eye
using only spaces for that seems a bit weird
unlike braces, they are used in normal language too
it works fine in lisp. but then the number of parentheses does not work fine in lisp unless you have a rainbowifier.
[1
-3
]``` list of 1 and -3, or list of -2
ah, good point
For import hooks, do you always need to import the import hook that will be doing the work first and then other modules like:
import import_hook
import a
import b
and another question, why does __future__ need to imported first on that topic? is it because it's an import hook? or does it enable flags for running other files and stuff like that
future does compile and parse time stuff
and your import hook has to be run to take effect, so it also needs to be executed (imported) before that
I see, thanks
future is for enabling some flags right? I feel like I read that somewhere but I can't remember
and there are some flags by default i.e. print_function
depends on what you mean with flags
it can do pretty much anything, just has to be implemented
Ah, so what does future do when you import say annotations, like a general 'roadmap'
I think the features are loaded after it gets the ast
then it just checks if the feature is enabled and constructs a string from it instead of doing the whole expression when compiling
But yes, you need to install the import hook before it can do its import hooking. And __future__ is not a real module, it's special syntax. It does affect parsing (in some cases), which is why it has to be at the very top.
!e It's also a real module, technically 😄
import __future__
print(__future__.__file__)
@raven ridge :white_check_mark: Your eval job has completed with return code 0.
/usr/local/lib/python3.10/__future__.py
!e
from __future__ import annotations
print('annotations' in globals())
print(annotations)
@dusk comet :white_check_mark: Your eval job has completed with return code 0.
001 | True
002 | _Feature((3, 7, 0, 'beta', 1), (3, 11, 0, 'alpha', 0), 16777216)
Okay, yes, but it doesn't contain the interesting parts :)
What if we made using global/nonlocal a SyntaxError (or at least SyntaxWarning) when used in a global scope?
>>> nonlocal x
File "<stdin>", line 1
SyntaxError: nonlocal declaration not allowed at module level
probably should be a warning for back compat, but i agree
see it quite often with newbie (and intermediate) code
Do you know why this doesn't happen with global?
wait, is 3.11 going to finally get None-aware operators? 
👀
I see the difference in bytecode, but does it actually make a difference? Is there a code example where having or not having it changes something? I guess theoretically LOAD_GLOBAL could be faster since it doesn't have to look at the locals, but since the locals and globals are anyways identical in the global scope I'd think LOAD_NAME doesn't actually look into the same namespace twice there..
Testing with timeit suggests the opposite, in fact:
In [9]: timeit.timeit(stmt='x += 1', setup='global x; x = 777')
Out[9]: 0.03630954201798886
In [10]: timeit.timeit(stmt='x += 1', setup='x = 777')
Out[10]: 0.01996866599074565
I'm pretty sure timeit, compile, etc assume function scope
So without global, it'd be STORE_FAST and LOAD_FAST instead of STORE_NAME and LOAD_NAME
Although after messing with dis a bunch, it seems that it's not the case
They don't assume any scope
Yeah, so my theory about why it would be faster doesn't hold up
I wish
That would really help
They don't go nearly far enough but it would be a great start
Though probably it won't go any further :-) but just for common use cases like the default argument dance
The opcodes aren't for any actual feature, it's purely an optimisation.
yeah, is None is a common check
Yeah that's true, I wouldn't be surprised if it were only an optimization for now
But it does give me some hope
Why do globals need to be a dict? I've spent an hour figuring out why my patch of a framework's dict that was replacing globals was working in one place but not the other until I found out they pass locals in only one eval
Both locals and builtins try PyObject_GetItem if they're not dicts, but globals only do PyDict_GetItemWithError which skips my magic
This was for eval/exec (which I forgot to mention until the end), eval is documented to only accept a dictionary for globals and LOAD_NAME does assume that
https://github.com/python/cpython/blob/46c7a6566bca2e974a89c90c35ed1c498d9d3b02/Python/ceval.c#L2992-L2993 while others try the object getitem if it's not a dict
Python/ceval.c lines 2992 to 2993
if (v == NULL) {
v = PyDict_GetItemWithError(GLOBALS(), name);```
I mean, definitely feels like an intentional interface at least from eval's side
!d eval
eval(expression[, globals[, locals]])```
The arguments are a string and optional globals and locals. If provided, *globals* must be a dictionary. If provided, *locals* can be any mapping object.
says that it must be a dictionary, I guess it could be ambiguous whether a subclass is a dict (and how it behaves) but it does somewhat make sense after reading
!e though this can be confusing
class A(dict):
def __getitem__(self, item):
return item
print(eval("invalid", A()))
print(eval("invalid", A(), {}))
@peak spoke :x: Your eval job has completed with return code 1.
001 | invalid
002 | Traceback (most recent call last):
003 | File "<string>", line 5, in <module>
004 | File "<string>", line 1, in <module>
005 | NameError: name 'invalid' is not defined
what is the byte code for checking if a: vs if len(a) > 0?
>>> dis.dis("if a: pass")
1 0 LOAD_NAME 0 (a)
2 POP_JUMP_IF_FALSE 4 (to 8)
4 LOAD_CONST 0 (None)
6 RETURN_VALUE
>> 8 LOAD_CONST 0 (None)
10 RETURN_VALUE
>>> dis.dis("if len(a) > 0: pass")
1 0 LOAD_NAME 0 (len)
2 LOAD_NAME 1 (a)
4 CALL_FUNCTION 1
6 LOAD_CONST 0 (0)
8 COMPARE_OP 4 (>)
10 POP_JUMP_IF_FALSE 8 (to 16)
12 LOAD_CONST 1 (None)
14 RETURN_VALUE
>> 16 LOAD_CONST 1 (None)
18 RETURN_VALUE
thanks! interesting
i think if a: is faster than if len(a):
and if len(a) is faster than if len(a) > 0:
Is any one free to review my repo? I made a tool to find unused import inside the python scripts. i think the overall design is vague.
isn't there a new POP_JUMP_IF_NONE bytecode instr coming soon?
It's merged.
https://github.com/python/cpython/pull/30019
there are lots of new opcodes coming. RESUME
ahh
are you gonna rewrite your none aware operator fork thing given the new bytecode instr?
!pep 505
basically:
var = a.b?.c
would be the same as
if a.b is not None:
var = a.b.c
i believe
tbh I like them, maybe I'm just used to how other languages do none aware / null coalescing
Also, where can I find the definitions of PyObject_*?
like I'm trying to look for where PyObject_IsInstance is defined
I found it in abstract.h but it's only the forward declaration
so if a.b was None, var wouldn't be defined at all? I can see that causing so many undefined variable issues in beginner code
Hmm, not sure let me check
intuitively I'd think it'd just be None
I mean, if it would be defined, it would have to be None, at which point why bother?
that's literally the same as var = a.b.c then
Parenthesised expressions are handled by the atom rule (not shown above), which will implicitly terminate the short-circuiting behaviour of the above transformation. For example, (a?.b ?? c).d?.e is evaluated as:
# a?.b
_v = a
if _v is not None:
_v = _v.b
# ... ?? c
if _v is None:
_v = c
# (...).d?.e
_v = _v.d
if _v is not None:
_v = _v.e
how do you search for files in github again
found it!
idk, I hate this syntax so much, it just looks really hard to read, python stands out in being an incredibly well readable language, I feel like this would hurt that readability
Yeah I think that was one of the reasons it was deferred
actaully it might not be
but I know a lot of people were opposed against PEP 505 (None aware operators) because of how unreadable they are
yeah, I'd be too
I feel like it shouldn’t be that unreadable as long as you can understand that “?” means that it can be none, especially with how useful non aware operators can be
I would personally love it, but I don't think it should be taught in say a beginners tutorial
and should probably have some PEP8 guidelines to keep it readable
I think having 4 extra if statements will hurt readability worse than ?
if a.b is None, var = a.b.c will throw an error
On the other hand, you can solve this without adding a new language construct. For example, you could make a function like getpath(x, "a", "b", "c") that would do x?.a?.b?.c.
there's ternary conditional which could be helpful to reduce the clutter, but yeah, none-aware operators would be nicer there. Problem is that people would almost certainly start using it even if it just replaces a single if and I feel like a single if x is None is more readable than ?
If a team finds that a single x?.y is worse than an if, they can add a rule to their linter settings
you can abuse any kind of syntax
yeah, I suppose there could be an option in linters to check for this, still though, I feel like in an average project, especially to beginners, this syntax would be way too confusing to understand and it just introduces extra overhead of looking up what it means
well... real-world projects don't employ beginners 🙂
yet beginners read the code of real-world projects
and you could say this about any new feature
open-source projects - yes, maybe, sometimes
? has a different issue though. It will only work for None, and not any other "error value". And it doesn't make handling exceptions easier.
hmm, if ? treated an exception in the same way it'd treat None, it would be pretty interesting
do other languages with none-aware operators do this?
no, but Haskell solved this in a general way in 1990s
Actually, Rust has a ? operator, which works for error values.
Rust doesn't have exceptions, so it signals failures with an error type, like Result<i32, SomeErrorType>.
yeah, I think I've heard of that, though only very vaguely. Was it something like having a func returning both the actual return value (or none) and an exception value which allowed those functions to basically be chained as long as that other value is not an error value, I may be completely wrong though, I haven't done any haskell, I just heard of something like this somewhere
sort of, yeah
For example, this is how you could emulate it in Python:
@none_aware
def foo():
x = yield f()
c = (yield (yield (yield x.a).b).c)
return c + 5
none_aware looks at the result of each yield. If it's None, it terminates the execution and simply returns None. Otherwise it passes the value back
that's interesting
yeah, but it's certainly an interesting idea
that's sort of how the original coroutines work, actually. so maybe not that bad
though it also wouldn't handle exceptions
it wouldn't
oh, and if you want to have an await chain, you also need a trainwreck like await (await (await foo()).bar()).baz()
yeah, I've seen these
If Python had inline generators, something like ```py
b = none_aware(send (send (send x).a).b)
Sometimes you can use this for fun and profit, actually
https://github.com/decorator-factory/py-bot-command-parser/blob/master/tests/test_basic.py#L166-L173
tests/test_basic.py lines 166 to 173
@do
async def point() -> tuple[int, int]:
await exact("(")
x = await integer
await exact(",")
y = await integer
await exact(")")
return (x, y)```
@grave jolt my very poor attempt at chaining functions even on exceptions: ```py
from unittest.mock import MagicMock
class ExcChain:
"""Sets up a decorator for a chain of exception aware functions."""
def __init__(self):
self.sentinel = object()
self.prev_exc = None
self.ret = self.sentinel
def reset(self):
self.prev_exc = None
self.ret = self.sentinel
def exc_aware(self, func):
def wrapper(*a, **kw):
if self.prev_exc is not None:
# Return callable mock so that we won't get errors on
# the actual chain
return MagicMock()
try:
# If we don't get an exception, store the return value so that we
# can get it in the end. Also return it so that the chain can continue
self.ret = func(*a, **kw)
return self.ret
except Exception as exc:
self.prev_exc = exc
return MagicMock()
return wrapper
def get_return(self):
if self.ret is self.sentinel or self.prev_exc is not None:
raise ValueError("No return value, chian failed or wasn't not started.")
return self.ret
exc_chain = ExcChain()
class Foo:
@exc_chain.exc_aware
def z(self):
return 10
class Bar:
@exc_chain.exc_aware
def y(self):
return Foo()
@exc_chain.exc_aware
def x():
print("x called")
return Bar()
@exc_chain.exc_aware
def foo():
return 1
@exc_chain.exc_aware
def bar():
return 0
@exc_chiain.exc_aware
def foobar(x, y):
return x / y
x().y().z()
ret = exc_chain.get_return()
exc_chain.reset()
foobar(foobar(foo(), bar()), foo())
ret = exc_chain.get_return()
so long as you're willing to decorate all of the involved functions, it should work (though I haven't tested it that much)
there are no caching self._repr, so this code calculate repr every time
or am I missing something?
Lib/asyncio/events.py line 71
self._repr = repr(self)```
we shouldnt cache repr before closing, because it may change
I don't get why it should be cached (why is that such a big issue)?
It seems to be a debug thing https://github.com/python/cpython/blob/3d11c1b8b49800c5c4c295953cc3abf577f6065a/Lib/asyncio/events.py#L67-L71
Lib/asyncio/events.py lines 67 to 71
if self._loop.get_debug():
# Keep a representation in debug mode to keep callback and
# parameters. For example, to log the warning
# "Executing <Handle...> took 2.5 second"
self._repr = repr(self)```
heyy a lil help pls
Write a python program to create a function which has operations to find cross product and dot product of two three dimentional vectors. Call the function in your program and use its functions.
you're in the wrong channel. please see #❓|how-to-get-help
oh okay thank you
If I pass a list object as a parameter to a class's __init__ and it is stored in the class instance, then does the list stored in the class instance update when I .append() the original list?
Or does it make an independent copy of the original list?
the only way to copy a list is to call the .copy method, or construct a new list using some other way.
assigning a list never copies it
ok
!e
x = 0
try:
x+=1
raise
except:
x+=1
print(x)
Has something like this ever been proposed where the state is saved before the try block and reverted to that state if an error occurs.
@pseudo cradle :white_check_mark: Your eval job has completed with return code 0.
2
It's not immediately intuitive that the above code should produce 2 instead of 1
One could argue that the onus is on the developer to make sure something unexpected doesn't happen like that, though.
Given how Python is though, with everything being at "runtime" per se, I think it makes sense to an extent how it's interpreted line by line until an error, and then it uses except
True, but while raise might be a bit obvious that it'll go into an except, something that relies on something else that's calculated during runtime (i.e. a variable not existing and raising NameError) might not be so obvious
so you'd just have weird behavior if sometimes you'd skip to the except block without evaluating the try block vs other times
It's impossible anyways. You can't reset the entire state. What if you make a network request/create a file/... in the try block.
Wait you want to rollback state?
it is impossible
if you sort a list, there are no way to shuffle it back
saving states will be very slow and not consistent operation
and the general consensus is to not put a lot of statements in your try, and only catch specific exceptions
Just for fun: this does roll the state back, at least state within Python.
import os
import sys
import inspect
from contextlib import contextmanager
@contextmanager
def atomic():
ppid = os.getpid()
cpid = os.fork()
if cpid:
os.waitpid(cpid, 0)
# we didn't get killed: error occured, skip body:
sys.settrace(lambda *a, **k:None)
frame = inspect.currentframe().f_back.f_back
def raiser(*a): raise RuntimeError
frame.f_trace = raiser
try:
yield
except RuntimeError:
return
try:
yield
except Exception:
# rollback: we exit
os._exit(0)
else:
# all good: kill state-preserved parent
os.kill(ppid, 9)
x = 23
with atomic():
x += 1
1/0
print(x)
That's so cursed I love it
^
Is it possible, even if it’s really ugly, to not execute code under a context manager when the file is imported? (Aside from obviously putting it in a func or under if main)
For example only execute the code under a context if you explicitly ask for it to be executed
not really, the __enter__ can raise an Exception, but it has to be handled if you want to not crash. If you want things like bytecode manipulation, ask in #esoteric-python
if you want conditional execution, consider a decorated function, sth like
def main():
...
@with_file('in.txt', really_execute=__name__=='__main__')
def _(f):
print(f.read())
...
Thanks
I think bytecode manipulation will be best. Since I would have to catch any exception, to not crash, it’ll just be as if I never raised the exception.
My code above literally does that
That’s awesome! Let me take a look
@quick snow
On phone so bit hard to read… is your code executing the code under the context and then rolling back the state or does it have the ability to completely ignore whatever is under the context and come back to it at a later “date”?
Both, kind of. I'll explain in #esoteric-python , because it really is esoteric
Ok, Thanks. Excited to see this work
Is there any built in way to overload methods or functions? Mainly a curiosity than anything else
I think functools provides an overload decorator
Yeah, typing.overload
It doesn't provide dispatching, only affects the typehints and IIRC even throws an exception if you attempt to run a decorated func
!e ```py
from typing import overload
@overload
def test(i: int):
print('int')
@overload
def test(i: str):
print('str')
test()
@spice pecan :x: Your eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 11, in <module>
003 | File "/usr/local/lib/python3.10/typing.py", line 1986, in _overload_dummy
004 | raise NotImplementedError(
005 | NotImplementedError: You should not call an overloaded function. A series of @overload-decorated functions outside a stub module should always be followed by an implementation that is not @overload-ed.
So that'd only really be used for structuring?
ye, it's a thing for type hints
functools.singledispatch actually checks the type of the first argument using its annotation
Only the first?
ye
there is a multiple dispatch decorator in a pypi package somewhere
but no builtin one to my knowledge
Yeah I've seen a couple
This is the one I found https://github.com/mrocklin/multipledispatch/
It was more a curiosity thing
note that properly speaking overloading is a static type thing by definition, and is separate from multiple dispatch/multi-methods
Fair enough
This looks dangerous https://www.python.org/dev/peps/pep-0679/
do people really use asserts in python
I did at first but then it rapidly started to become pointless
In testing, yes
that's a different thing
the asserts provided by test frameworks are not the same thing as the built in language assert
unittest pytest uses assert statements