#internals-and-peps
1 messages · Page 129 of 1
I was thinking of making TypeScript-but-in-Python with a reworked type system, but I'm too lazy and incompetent
Typethon
isn't this advanced discussion, not esoteric python lol
yea tbh i sent that when i was half asleep and didnt check the channel
ah lol
I agree, it works for my usecase. In deed, what I'm trying to do is multiple language support without using gettext because I was too lazy to learn how to use that. So... language classes with string constants (feel free to share better methods). For that particular use case it has its limitations of course (plurality and a whole lot of gramatical issues) but for like the example given its really comfy 
>>> (-1)**(1/2)
(6.123233995736766e-17+1j)
```what
is this fpa weird crap or did i do something wrong
the real part is very close to zero, probably just rounding issues
yeah, this is another case of floats being inaccurate
@charred pilot @flat gazelle i agree, i tried using cmath.sqrt and it gave me a clean and precise 1j
whereabouts would I go to ask Celery questions?
um, the documentation is literally wrong 🤣
they named it __truediv__, not __div__
should i open up an issue on a cpython repo? if so, where? (since this is documentation issue)
how would i open an issue? is there some sort of registering process i have to undergo?
I think you can create an account through github on the left, then just create a new issue with the doc component
okay, thank you!
submitted my first bug report :D
thanks again!
i'm sure there's more where that came from 😉
so maybe it's conceivable to have a language for AST-manipulations
Not really about direct manipulations, but I've worked on this topic for a while writing ReizQL. It is basically a pattern matching DSL specifically designed to match ASTs, which then can be later used as a source code search query but as well as getting accepted by numerous other tools to do refactorings on top of the AST. https://reizio.readthedocs.io/en/latest/reizql.html
nice!
It is indeed kind of hard to extend if you want complicated tokenization rules, but most of the time what you want is just adding single characters since beside numbers/identifiers/strings tokens tend be very simple (not the tokenization as a whole, especially for a language like Python where you manage the state of the indentation).
in theory, you could. From the eye of the parser, f-strings are not much different than regular strings in terms of tokens. But during the parsing phase, when the parser sees a string it checks it's mode ((f/b)[r]) and acts upon it. If it sees an f-string, it calls the f-string parser located in string_parser.c which effectively creates the AST for it.
Parser/string_parser.c line 277
// FSTRING STUFF```
you could simply parse/eval it by adding "f" infront of the string's repr().
Don't F-strings simply get converted from ```
f"Hello {name}, {greeting}!"
"Hello " + name + ", " + greeting + "!"
there's nothing "simple" there
well, i liked my lambda solution 🙂
Ah true, I didn't mean it in that sense. Accomplishing that is somewhat hard, but understanding what it does generally is simple.
It turns it into string concatenation
!e ```py
from ctypes import pythonapi, py_object, sizeof
PyType_Modified = pythonapi.PyType_Modified
PyType_Modified.argtypes = [py_object]
def getdict(cls):
dct = cls.dict
if type(dct) == dict:
return dct
return py_object.from_address(id(dct) + 2 * sizeof(py_object)).value
old_format = str.format
def new_format(self, *args, **kwargs):
if args:
return old_format(self, *args, **kwargs)
return eval('f' + repr(self), {**globals(), **kwargs})
getdict(str)['format'] = new_format
PyType_Modified(str)
pattern = "The tournament '{name}' is {'individual' if teamSize == 1 else f'in teams of {teamSize +1}'}"
print(pattern.format(name='One', teamSize=2))```
@pliant tusk :white_check_mark: Your eval job has completed with return code 0.
The tournament 'One' is in teams of 3
^ a quick and dirty implementation where it just uses eval
is there any kind of tool that makes it easier to test out changes to python's grammar? as opposed to regenerating/recompiling every time
How would you make a script that can do grammar jacks for you?
in roblox for army groups
It's actually more like:
"".join(["Hello ", format(name), ", ", format(greeting), "!"])
Except there's a bytecode for formatting each value (and applying !s/!r/!a), and another to join the segments on the stack.
Didn't mean to dump on your work btw. It just doesn't suit my own purposes — but I'm hardly qualified to make any meaningful judgements in general
Functionally, there really isn't much point.
I would like privacy, myself, because it would draw an unambiguous line in the sand stating "here be dragons". Going past that line takes you out of the area that an API has built safety measures and error checking into. This lets you write a public API which is safety checked within reason, and a private codebase which you the original creator have no obligation to write safety checks or even semantic code. You can be as idiosyncratic as you like.
I just feel it would make life a little easier, much like runtime type enforcement (go, beartyping, go!)
But it wouldn't really change much in the long run, I don't think.
That already exists today, with the underscore prefix on names
If all you want privates for is to communicate to users what's safe and intended for end users to use and what's not, then that's exactly what the _ prefix indicates to everyone today.
Ehhhhhhhhhh. You're not wrong
But access to the private information at all can confuse the issue. It's a matter of philosophy but I don't feel it's a strong enough boundary.
@summer forge you could use encapsulation...
class Foo:
__private: None
_protected: None
public: None
The purpose of __ is to prevent name collisions between "private" names, you can still access them perfectly well (_Foo__private)
The purpose of private/public modifiers in languages isn't to absolutely prevent someone from accessing private fields/methods (it's still possible in most cases), but to separate the public interface from the private implementation
where is __length_hint__ actually used? since __length_hint__ is not required to be accurate, how does it actually help? i'd like to see some stuff where __length_hint__ is put to use for optimization
iterators
constructing lists from them, for example
since its not guaranteed to be accurate, how is it used?
it is used to preallocate space
so if it is accurate it speeds up object construction
for example, tuple(obj) calls obj.__length_hint__ in order to allocate the initial tuple
if more items are yielded then the length_hint it reallocates more space and trims at the end
Objects/tupleobject.c lines 738 to 751
static PyObject *
tuple_new_impl(PyTypeObject *type, PyObject *iterable)
/*[clinic end generated code: output=4546d9f0d469bce7 input=86963bcde633b5a2]*/
{
if (type != &PyTuple_Type)
return tuple_subtype_new(type, iterable);
if (iterable == NULL) {
return tuple_get_empty();
}
else {
return PySequence_Tuple(iterable);
}
}```
Objects/abstract.c lines 2073 to 2079
/* Guess result size and allocate space. */
n = PyObject_LengthHint(v, 10);
if (n == -1)
goto Fail;
result = PyTuple_New(n);
if (result == NULL)
goto Fail;```
so tuple(v) calls PySequence_Tuple(v) which allocates based on __length_hint__ @valid rose ^
used in list.extend too
ah thanks :)
well, in that sense, microservices rule
if you want to enforce privacy between teams, microservices are basically the pinnacle
can't be more private than "i only communicate via http, don't even try anything else, otherwise i call security"
find a RCE vulnerability in the other microservice and instead of reporting it, use it to read private fields /
calls security :p
print(len("ISGAYME"))
?
I want to make a 3d animation in python
It involves a 3d camera moving in 3d space surrounded by small spheres and ellipsoids. Is there any library to do this kind of 3d animation? Currently, I am doing it in matplotlib but it is cumbersome and the final results don't look so nice
Blender has a Python language you can use
Can anyone plz suggest me how to learn dsa from scratch provided that I am extremely new to programming
I want to learn in C langauge
they aren't really a beginner topic, you should be at least a little familiar in your target language before trying to learn them, especially if you've never programmed before
Is there a nicer way of doing something like this
class Options:
class Number:
def __init__(self, value: int):
self.value = value
class Lowercase:
def __init__(self, value: str):
self.value = value
class Uppercase:
def __init__(self, value: str):
self.value = value
OptionType = Union[Number, Lowercase, Uppercase]
def process(option: OptionType):
if isinstance(option, Options.Number):
print(option.value + 1)
elif isinstance(option, Options.Lowercase):
print(option.value[:-1])
elif isinstance(option, Options.Uppercase):
print(option.value[0])
process(Options.Number(1)) # prints 2
process(Options.Uppercase('HELLO')) # prints H
Basically I want a sort of construct (like OptionType here) that can contain multiple values (like Number) which can each be created to contain a value of it's specific type.
(I'm not suggesting any specific semantics i'm looking for here, i'm just trying to give a code example which might help explain sort of what i'm looking for)
So you want a tagged union?
I'd do ```py
class Number(NamedTuple):
value: int
class Lowercase(NamedTuple):
value: str
class Uppsercase(NamedTuple):
value: str
OptionType = Union[Number, Lowercase, Uppercase]
(or use a frozen dataclass)
I just spend a hundred words trying to describe that and you did it in two
I have a cursed snippet which allows ```py
class Option(SumType):
Number(int)
Lowercase(str)
Uppercase(str)
it dynamically defines a meta-metaclass, but I don't remember why
Thanks, that seems like quite a neat way of doing it. Would you say chucking it in an Options class is an OK way of namespacing it, smth like
class Options:
class Number(NamedTuple):
value: int
class Lowercase(NamedTuple):
value: str
class Uppercase(NamedTuple):
value: str
_OptionsType = Union[Number, Lowercase, Uppercase]
OptionsType = Options._OptionsType
Probably Option is better than Options
I'd just go with a flat structure
Don't you need to do Union[Options.Number, Options.Lowercase, etc]?
No, in the class body they're not yet namespaced
Oh wait yeah, the union is defined inside the class
Any particular reason or are you just thinking the namespacing would usually be unnecessary. I'm thinking some of the options might directly correspond to a class, (e.g. a Counter option that would contain a collections.Counter), so namespacing would make things a bit clearer.
No particular reason, just feels more natural
I think there were some plans for a @sealed decorator, but I don't think it's there yet
it would create a sealed class (i.e. a class that can only be subclassed by classes in this module)
Coooooool
You already haveave them in a file though right?
at the moment it's in a file with a bunch of other code, putting it in a separate file to namespace it would probably be a neater solution, yeah. I was sort of trying to avoid creating a new file just for it though because it's not a massive part of the code.
python really needs "namespaces" that aren't "classes"
named tuple?
meh
modules?
yes exactly, but not 1:1 with files
i suppose you can use ModuleType for this?
but ModuleType expects a ModuleSpec and some other stuff that doesn't really make sense
How would the usage look?
module Option:
...
i'm honestly not sure, i still don't fully understand the import system
or yeah, use module as a keyword
or something as mundane as
my_module = Module(
a=1,
b=2,
)
my_module.c = 3
my_module.__getattr__ = lambda key: ...
although having a true module keyword like that would be really interesting when it comes to lexical scoping
# foo.py
x = 1
module A:
from foo import x
y = x + 1
There's types.SimpleNamespace
aha, that's the one
but there's not much tool support for it
and you can't do class, def etc. inside of it
that seems fine in this case though
using classes as namespaces is a bit strange (because a class is a type, not a namespace), but I guess a namespace is just a class... without some features
!e
I have a hack up my sleeve :^)
from types import SimpleNamespace
def namespace(genf):
gen = genf()
next(gen)
return SimpleNamespace(**gen.gi_frame.f_locals)
@namespace
def banana():
foo = 1
def bar():
return foo
class Baz:
pass
yield
print(banana)
@grave jolt :white_check_mark: Your eval job has completed with return code 0.
namespace(bar=<function banana.<locals>.bar at 0x7fc3289c1d30>, Baz=<class '__main__.banana.<locals>.Baz'>, foo=1)
this is pure evil
i was about to do the same thing with a metaclass
and honestly no i dont think its that evil
oh, well gi_frame is evil
yeah lol, that's the whole thing
I wonder if I can write a mypy plugin for this
sounds hard
!eval
from dataclasses import dataclass
from types import SimpleNamespace
class Namespace(type):
def __new__(meta, name, bases, attributes):
return SimpleNamespace(**attributes)
class options(metaclass=Namespace):
@dataclass
class BaseOption:
value: object
class Number(BaseOption):
value: int
class Lowercase(BaseOption):
value: str
class Uppercase(BaseOption):
value: str
print(options.Number)
@paper echo :white_check_mark: Your eval job has completed with return code 0.
<class '__main__.options.Number'>
i think i would legitimately use this
not sure if it's possible to make it prettier than metaclass=Namespace though
class NamespaceMeta(type):
def __new__(meta, name, bases, attributes):
return SimpleNamespace(**attributes)
class Namespace(metaclass=NamespaceMeta):
pass
class options(Namespace):
...
don't even need a mypy plugin: https://mypy-play.net/?mypy=latest&python=3.10&gist=dbbfa30a4a3171274b29606430d14d0d
ah i wasn't sure if that would work, not sure what i was afraid of
I mean my thing
maybe Namespace should also implement __subclasshook__ and/or __instancecheck__?
in your case, mypy will complain if you define a function (i.e. functions won't accept self), and Callable types will also be bonked
you know about Callable behaviour in classes with mypy?
!e
Found a way to not require the yield -- that would be so error prone!
from types import SimpleNamespace
from contextlib import suppress
def namespace(corof):
coro = corof()
frame = coro.cr_frame
with suppress(StopIteration):
coro.send(None)
return SimpleNamespace(**frame.f_locals)
@namespace
async def banana():
foo = 1
def bar():
return foo
class Baz:
pass
print(banana)
@grave jolt :white_check_mark: Your eval job has completed with return code 0.
namespace(bar=<function banana.<locals>.bar at 0x7f95f2378f70>, Baz=<class '__main__.banana.<locals>.Baz'>, foo=1)
I think the best thing there is would be __prepare__
yeah but you still have method binding behavior and stuff right?
unless you use that to wrap every function in staticmethod()
yes 😦
I feel every muscle on that smiley's face
!e had to add a special case:
from dataclasses import dataclass
from types import SimpleNamespace
class NamespaceMeta(type):
def __new__(meta, name, bases, attributes):
if name == f'{__name__}.Namespace':
return super().__new__(meta, name, bases, attributes)
else:
return SimpleNamespace(**attributes)
class Namespace(metaclass=NamespaceMeta):
pass
class options(Namespace):
@dataclass
class BaseOption:
value: object
class Number(BaseOption):
value: int
class Lowercase(BaseOption):
value: str
class Uppercase(BaseOption):
value: str
print(options.Number)
@paper echo :x: Your eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 14, in <module>
003 | TypeError: no positional arguments expected
oops still broken
hm, i wonder where that's coming from
oh it's not a fully qualified name
!e ```python
from dataclasses import dataclass
from types import SimpleNamespace
class _NamespaceMeta(type):
def new(meta, name, bases, attributes):
if name == 'Namespace':
return super().new(meta, name, bases, attributes)
else:
return SimpleNamespace(**attributes)
class Namespace(metaclass=_NamespaceMeta):
pass
class options(Namespace):
@dataclass
class BaseOption:
value: object
class Number(BaseOption):
value: int
class Lowercase(BaseOption):
value: str
class Uppercase(BaseOption):
value: str
print(options)
print(options.Number)
that seems super flaky
@paper echo :white_check_mark: Your eval job has completed with return code 0.
001 | namespace(__module__='__main__', __qualname__='options', BaseOption=<class '__main__.options.BaseOption'>, Number=<class '__main__.options.Number'>, Lowercase=<class '__main__.options.Lowercase'>, Uppercase=<class '__main__.options.Uppercase'>)
002 | <class '__main__.options.Number'>
Who even used div?
I swear I only remember using truediv
yesterday i was browsing through the documentation (in version 3.9) when i saw __div__ being mentioned - it's not supported anymore in py3 since true division was enabled by default. so i opened up an issue on bpo, made a pr to change __div__ to __truediv__, and it got merged
what is bpo?
yep, i'm thinking of becoming a documentarian contributor once i get advanced enough
i love python, so actually contributing to it feels amazing
super cool!
Damn I wish I was as good as you all lol
Every time I learn something I always just go steps backward
Since I didn’t learn another concept
you will be :D
we all get stuck, sometimes i sleep and the next day i suddenly understand something
it's natural lol
But it feels like I’m stuck at intermediate since every time I learn something new I have another concept I missed that I need to learn
flashbacks to the decorator days
that really blew my mind, seeing functions can be inputs and outputs to other functions
Decorators really stumped me lmao
that's the process of learning :P the more you know, the more you know you don't know
actually, i believe there's a study on this
lmao i have no idea what that is
google search time
I’m usually not good at contributing if I don’t know most of the codebAse
Not for me cause I already knew the effect when I started learning
So I always perceive me thinking I’m bad as a placebo effect to me thinking I’m good
I just realized this isn’t the cat dev server oops
i remember in cat devs we have this ot name "wait-this-isnt-pydis"
now i guess we'll have "wait-this-isnt-cat-devs"
!otn a wait this isnt cat devs
This is my favorite representation of the dinning Kruger effect
reminds me of the uncanny valley
this is going a bit off-topic, let's move to #ot0-psvm’s-eternal-disapproval lol
why he don't need work
the fun part about the DK effect is that the pop-sci interpretation of it is incorrect and it is questionable if it exists in the first place. but i like this chart a lot
the thing is that not everyone ends up on the "child's hill"
in fact i'd argue that ending up on said hill is an impediment to learning and that the most effective learners don't go on the hill at all
TIL that complex literals do not exist, only imaginary literals do, which is why stuff like 2+3j / 4+5j is not equivalent to (2+3j) / (4+5j).
since there are no complex literals, they're not considered atoms and thus are not considered primaries, so 3j / 4 takes priority as m_expr and + is regarded as a normal operator
AHHH PYTHON IS JUST SO DAMN COOL
This is how calculators work though? What will happen is 2 + (3 * j/4) + (5 * j)
yeah, i think they all parse it the same way
i used to think that 2 + 3j was ONE THING
like, python recognizes it as one atom
Oh haha, no it follows the rules of prioritisation in math. So first 3 * j then 2 + result
!e
well, it is not quite that simple
from ast import literal_eval
print(literal_eval('1+1j'))
@flat gazelle :white_check_mark: Your eval job has completed with return code 0.
(1+1j)
or well, it is, literal_eval just is weird
would be weird if that or -1 didn't work
How do you specify an empty set?
!e
from ast import literal_eval
print(literal_eval("set()"))
@grave jolt :white_check_mark: Your eval job has completed with return code 0.
set()
yeah, it seems to special case some things
it really doesn't
spec meh
you can just instantiate an empty ModuleType
no, it requires a name
that's all
def _build_mod(
*,
name,
code,
initial_globals: Optional[Dict[str, Any]],
...
) -> ModuleType:
default
mod = ModuleType(name)
mod.__dict__.update(initial_globals or {})
mod.__file__ = str(module_path)
that's the main func to build modules manually in use()
!e ```python
from types import ModuleType
from typing import Any, Optional
def build_mod(name, initial_globals: Optional[dict[str, Any]]) -> ModuleType:
mod = ModuleType(name)
mod.dict.update(initial_globals or {})
return mod
hello = build_mod('hello', dict(a=1, b=2))
def do_thing():
raise RuntimeError('testing')
hello.do_thing = do_thing
hello.do_thing()
@paper echo :x: Your eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 15, in <module>
003 | File "<string>", line 12, in do_thing
004 | RuntimeError: testing
hm, that didn't clarify anything
let me try it in an actual file
i'm curious how the tracebacks work
!e ```python
import logging
from types import ModuleType
from typing import Any, Optional
def build_mod(name, initial_globals: Optional[dict[str, Any]]) -> ModuleType:
mod = ModuleType(name)
mod.dict.update(initial_globals or {})
return mod
hello = build_mod('hello', dict(a=1, b=2))
logger = logging.getLogger(name)
def do_thing():
logger.info('testing')
hello.do_thing = do_thing
if name == 'main':
logging.basicConfig(level=logging.INFO)
hello.do_thing()
@paper echo :white_check_mark: Your eval job has completed with return code 0.
INFO:__main__:testing
so there's no way to define something "inside" this module using this technique
but that's because you don't care, your library does the spec/exec business
can i get hello to somehow "take over" the do_thing function?
yeah, how those modules come to live it's one of the weirdest things i've ever seen in python
heh
no, seriously
the whole function:
def _build_mod(
*,
name,
code,
initial_globals: Optional[Dict[str, Any]],
module_path,
aspectize,
default=mode.fastfail,
aspectize_dunders=aspectize_dunders,
package=None,
) -> ModuleType:
mod = ModuleType(name)
mod.__dict__.update(initial_globals or {})
mod.__file__ = str(module_path)
code_text = codecs.decode(code)
# module file "<", ">" chars are specially handled by inspect
if not sys.platform.startswith("win"):
getattr(linecache, "cache")[f"<{name}>"] = (
len(code), # size of source code
None, # last modified time; None means there is no physical file
[
*map( # a list of lines, including trailing newline on each
lambda ln: ln + "\x0a", code_text.splitlines()
)
],
mod.__file__, # file name, e.g. "<mymodule>" or the actual path to the file
)
# not catching this causes the most irritating bugs ever!
if package:
mod.__package__ = package
try:
exec(compile(code, f"<{name}>", "exec"), mod.__dict__)
except: # reraise anything without handling - clean and simple.
raise
for (check, pattern), decorator in aspectize.items():
Use._apply_aspect(
mod, check, pattern, decorator, aspectize_dunders=aspectize_dunders
)
return mod```
"not catching this causes the most irritating bugs ever!"
yeah you basically exec the code with the module temporarily set to the global namespace right?
yeah, basically, but that bug mentioned there
if there's an exception during that step, everything just stops right there, it drops out of all the outer functions
code outside isn't executed
you can probably set do_thing.__module__ = 'hello'
weirdest bug ever
...what
>>> print("\u00C7")
Ç
>>> print("\u0043\u0327")
Ç
>>> "Ç" == "Ç"
False
```unicode codepoint confusion lol
yeah, exactly my reaction when i saw that
to that
this would be super fun to use to troll people 
the cyrillics are a method of the past.
embrace: precomposed vs composing unicode!
this is a legitimate issue with unicode @nova iris . it's very important to sanitize unicode inputs with https://docs.python.org/3/library/unicodedata.html#unicodedata.normalize
my standard "data scientist who doesn't trust anyone with data" text cleaning protocol always started with unicodedata.normalize and str.casefold
i like that ^^
helper: "this is an actual unicode issue, use unicode normalization and casefold!"
non-helper like me: "how can i use this to bamboozle my friends?"
lmao
jokes aside, thanks for the tips salt! i never knew that there were literally methods dedicated to resolving this issue
i've known about str.casefold, but unicodedata.normalize is cool
of course! and here's some "light reading" on the subject:
https://www.unicode.org/reports/tr15/tr15-18.html
https://unicode.org/faq/char_combmark.html
https://www.unicode.org/reports/tr29/
https://en.wikipedia.org/wiki/Unicode_equivalence
https://en.wikipedia.org/wiki/Unicode_compatibility_characters
https://www.unicode.org/versions/Unicode6.0.0/ch02.pdf#G11062
https://www.unicode.org/reports/tr44/
.bm 880537801970569267
oh right, my pr isn't merged (for bm to be used everywhere)
i have to stop procrastinating and work on it so it gets merged lmao
is .bm broken?
no, it's just using the default whitelist, i have a pr right now aiming to resolve this issue and permit everyone to use it everywhere
thank you very much! i'll start reading soon!
!e overwriting __module__ doesn't seem to do the job for logging
import logging
from types import ModuleType
from typing import Any, Callable, Optional, TypeVar
def build_mod(name, initial_globals: Optional[dict[str, Any]]) -> ModuleType:
mod = ModuleType(name)
mod.__dict__.update(initial_globals or {})
return mod
_Callable = TypeVar('_Callable', bound=Callable)
def in_module(module: ModuleType) -> Callable[[_Callable], _Callable]:
def decorator(func: _Callable) -> _Callable:
module.__dict__[func.__name__] = func
if module.__package__ is None:
func.__module__ = f'{module.__name__}'
else:
func.__module__ = f'{module.__package__}{module.__name__}'
return func
return decorator
logger = logging.getLogger(__name__)
hello = build_mod('hello', dict(a=1, b=2))
@in_module(hello)
def do_thing():
logger.info('testing')
if __name__ == '__main__':
logging.basicConfig(level=logging.INFO)
do_thing()
hello.do_thing()
print(do_thing.__name__)
print(do_thing.__module__)
@paper echo :white_check_mark: Your eval job has completed with return code 0.
001 | INFO:__main__:testing
002 | INFO:__main__:testing
003 | do_thing
004 | hello
I had a patch around somewhere that replaced the method used to find the module name with my own version
ah it was a context mgr, it looks like logging itself gets it through the code attr
@contextmanager
def patch_log_module(logger: logging.Logger, module_name: str):
"""Patch logs using `logger` within this context manager to use `module_name` as the module name."""
original_find_caller = logger.findCaller
def patched_caller(self: logging.Logger, stack_info: bool) -> Tuple[str, int, str, Optional[str]]:
_, lno, func, sinfo = original_find_caller(stack_info)
return module_name, lno, func, sinfo
logger.findCaller = MethodType(patched_caller, logger)
try:
yield
finally:
logger.findCaller = original_find_caller
ah, so you'd have to wrap the function to invoke this context manager before every call
what opinions do people here have on loguru? I have remember hearing about it somewhere, and it looks kinda nifty, and I'm wondering why it isn't sometimes (if it all) mentioned when talking about logging things
It's just a wrapper over logging, I think it's cool
Ye, it seems nicer than logging
it's been on my to-investigate list
I did a google search:
The main difference is that standard logging requires the user to explicitly instantiate named Logger and configure them with Handler , Formatter and Filter , while loguru tries to narrow down the amount of configuration steps.
from the loguru website
I mean, so logging.basicConfig basically?
I dunno my interest is not piqued by that description
i've said before that logging.basicConfig just needs a logger= kwarg so you can specify what logger to operate on
I guess, writing something like basicConfig that operates on an arbitrary logger is like 10-20 lines of code
I don't see anything terrible about loguru though I'm not 100% convinced the author actually understands the point of logging's hierarchical design, it's a pretty old and good idea that comes from one of the Java loggers.
I probably would be fine to use it if it were nearly as widely used as logging
But to tie yourself to a third party library with 5% of the google-ability and standardity (real words), just to save a handful of convenience lines of code, seems very not worthwhile to me
Sequences compare lexicographically using comparison of corresponding elements. The built-in containers typically assume identical objects are equal to themselves.
python docs is really letting me discover things i would've never otherwise learned about/thought possible
so, let me present to y'all advanced discussers some fun, cursed (but perhaps actually useful?) code:```py
[1, 2] > [3, 4]
False
[5, 6, 7] > [4, 5, 6] # 5 > 4
True
[5, 6, 7] > [5, 6, 8] # 7 < 8
False
[0, "incompatible"] > [0, -2]
Traceback (most recent call last):
File "<pyshell#78>", line 1, in <module>
[0, "incompatible"] > [0, -2]
TypeError: '>' not supported between instances of 'str' and 'int'
[1, 2, 3] < [1, 2] # longer
False
nan_obj = float("nan")
[nan_obj] == [nan_obj] # bypasses equality check (can of worms here)
True
by "identical" there, it means an is check?
yep
[...] all objects should be reflexive (i.e. x is y implies x == y).
python: *introduces nan* i've lived long enough to see myself become the villain.
I remember seeing something about how the statistics module can take in float("nan") and then is ignored
lmao why python- stop your idealistic reflexivity dreams
you ruined your dreamscape with nan. face the consequences.
I wish I could access the __code__ object of float.__eq__ so i could see what's happening
@nova iris you have any idea how to do this?
it might give an explanation as to why nan works that way
floats are done in C, you have to look at the source on github
ah
Thanks
well float("nan") is truthy, so that might mean something? idk honestly lmao
oh, nan always compare unequal to itself. but when placed in a container such as a list, dict, or set, they use what's called an "assumed reflexivity" standard, which basically means that any two objects who are the same object (identity) are implied to be equal (equality), so the container comparison bypasses the equality check
they do this for performance improvements
I'd guess they just use normal C comparisons on the underlying floats
and that's why [nan] == [nan] is true, since nan points to the same object
but nan == nan is not, because nan.__eq__(nan) returns False no matter what
nope, but what @peak spoke said is probably the only way
so what if you have mutable objects within an object that you're comparing, like would this be the case?
[[1, 2]] == [[1, 2]]
>>> False
```?
!e
[[1, 2]] == [[1, 2]]
>>> False
@surreal sun :x: Your eval job has completed with return code 1.
001 | File "<string>", line 2
002 | >>> False
003 | ^
004 | SyntaxError: invalid syntax
!e ```py
print([[1, 2]] == [[1, 2]])
@surreal sun :warning: Your eval job has completed with return code 0.
[No output]
!e ```py
print([[1, 2]] == [[1, 2]])
@surreal sun :white_check_mark: Your eval job has completed with return code 0.
True
well, they're compared lexicographically all the way down, iterating through a new nested container when it sees one
it's pretty hard to explain, the docs are super good https://docs.python.org/3/reference/expressions.html#value-comparisons
ty
🇳🇵
As I dig into the rabbit hole of unknowns on my list tonight; anyone have a link/summary of what from __future__ import annotations does?
It's one of those things I use when making class factories and, gosh darnit, I don't know why 
makes your annotations act like forwardrefs implicitly
OKAY WAIT. WHAT. THE. FK.
I NEEDED THIS BUT I NEVER KNEW YOU COULD DO THIS
__future__is a real module
Official docs starting off great making me feel comfy.
i always use def f(self) -> "A" instead
so every annotation is handled as if you did name: "ann" and if you need to access the underlying result it has to be evaluated first
Interdasting. I didn't know you could use a literal in place
For container types such as list, tuple, set, frozenset, dict, or collections.deque, the expression x in y is equivalent to any(x is e or x == e for e in y).
here, the reflexive nature makes sense, since it's membership testing
!e
class No(dict):
def __init__(self):
pass
def __missing__(self, key):
print(key)
class Change(type):
def __prepare__(*args, **kwargs):
return No()
class ScrewedUpClass(metaclass=Change):
def __init_subclass__(cls, **kwargs):
pass
class Foo(ScrewedUpClass):
x: "imagine"
@surreal sun :white_check_mark: Your eval job has completed with return code 0.
001 | __name__
002 | __name__
ah, the classic weird namespace + __prepare__ + __init_subclass__ combo
actually wait, you don't need __init_subclass__ there?
you aren't passing in any **kwargs
!e
class No(dict):
def __init__(self):
pass
def __missing__(self, key):
print(key)
class Change(type):
def __prepare__(*args, **kwargs):
return No()
class Foo(metaclass=Change):
x
@surreal sun :white_check_mark: Your eval job has completed with return code 0.
001 | __name__
002 | x
nice, it works
metaclass fun
next up: metametaclass
actually, i wonder if an actual metametaclass has ever been used in production code
hey, fun fact everybody
all mentions of the word "metametaclass" are in #esoteric-python and #internals-and-peps 🤣
the problem is that it's hard to learn for newbies, and honestly it's 20 ugly lines
that's 20 lines of boilerplate to copy and paste into every project
lol, this is cool
!e
from __future__ import annotations
@lambda c:c()
class __annotations__:
def __setitem__(self, name, value):
globals()[value] = globals()[name]
oops: i_set_x_to = 5
print(i_set_x_to)
@surreal sun :white_check_mark: Your eval job has completed with return code 0.
5
day by day, you and i, we stray further from the light of the python gods
what is this cursed insanity
not that cursed tbh
i've seen worse
i can make it more cursed tho
let's see
make a whole damn language out of annotationsl mao
"not that cursed"
meanwhile, literally uses __annotations__ to set values-
actually, can you do an interesting experiment? remove the lambda decorator, and instead use __class_getitem__ as the dunder method - i want to see if it works, and it'd probably be a better solution if it does
oh wait nvm
me dumb
class_setitem doesn't exist
only class_getitem does
from __future__ import annotations
@lambda c:c()
class __annotations__:
def __setitem__(self, name, value):
globals()[value] = globals()[name]
imagine_being_objects: couldnt_be_me = 5
It does?
arbitrary expressions in decorators is new to 3.9
How does it work if it finds an arbritary expression? would it just wrap the arbritary expression around the actual function, and modify the func to be func() instead of func
it just calls the result of that expression
tbh why should i use that??
it's useful for how I'm utilizing it, where it'll override __annotations__(), as the decorator over the class will change whenever it is called to __annotations__() instead of __annotations__
((I think
mhm ic
@unkempt rock :white_check_mark: Your eval job has completed with return code 0.
001 | <class '__main__.A'>
002 | <__main__.B object at 0x7f26f7d63fd0>
you shouldn't
lmao
@halcyon trail https://github.com/python/cpython/compare/main...gwerbin:gwerbin/basicconfig-any-logger
why not bro?
For most use cases you don't wanna use it
!eval ```python
@3
def f():
return
@paper echo :x: Your eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 2, in <module>
003 | TypeError: 'int' object is not callable
but i can just call the class
because doing it through a normal assignment is infinitely clearer
i know what you mean
what's your favorite static type checker
Why did people get so mad at Guido that they pretty much pushed him to resign over the walrus operator? what was so bad about the walrus operator
they couldnt handle the walrus
hello i am looking to download some images which are in 3 to 4 pages of the the website ? which library should be suitable to do the job?
__WALRUS_DEFAULT_NAMESPACE = globals()
def walrus(name, value, namespace=__WALRUS_DEFAULT_NAMESPACE):
namespace[name] = value
return value
this was a lot clunkier, can't believe I used it for years, I'm glad for the syntactic sugar
holy shit generics are the coolest thing
they get good with HKTs 🙂
Do you prefer functions-as-objects, or objects that implement __call__? Is there a reason I should prefer one or the other? (Obviously the actual objects if they have internal state)
Generally I would go with a bound method instead of __call__, but as a rule of thumb if you are mutating things of that function, you should use an object instead of a function.
It depends on what it represents, is it an object in of itself but can be called?
Or is it mostly a function but you need some attributes?
Huh, I didn't realize you could give functions attributes
What's an actual use case of that?
memoisation
ah, interesting
i kinda hate __call__ because you can't git grep where it's called
yeah, it is generally much better (even just for readability once it gets passed into more functions) to do
obj.method_as_a_callable
Starlette has a really weird convention that functions and methods are interpreted as request -> response functions, and instances of custom classes are interpreted as ASGI apps. It's sometimes a bit annoying tbh
although I suppose most of the time you operate with request->response functions
sounds like http4k
I quite like the abstraction
On the other hand, "function from Animal to String" is a subtype of "function from Cat to String" because the function type constructor is contravariant in the parameter type. Here the subtyping relation of the simple types is reversed for the complex types.
... what
Hello,
This is more related to Django at the end of the day is Python and I would like to know your opinion about Models Managers. Do you think that Model Managers in Django should return only querysets or is okay to return also objects? An example below:
class FixturesManager(models.Manager):
def next_fixture(self, competition: Competition=None) -> Fixture:
qs = self.get_queryset()
if competition:
qs = qs.filter(matchday=competition.current_matchday).filter(competition=competition)
return qs.exclude(status='FINISHED').order_by('utc_date').first()
def matchday_fixtures(self, matchday: int, competition: Competition=None) -> QuerySet:
qs = self.get_queryset()
if competition:
qs = qs.filter(competition=competition)
return qs.filter(matchday=matchday).order_by('utc_date')
If you don't understand how contravariance works, I can explain 🙂
thanks ya im having trouble understanding the wiki explaining contravariance.
i think i see what it's saying logically, like if functions themselves act contra to the normal subtyping direction. then sure f(animal)->str is a subtype of f(cat)->str. but i have no idea why they would make it that way or why it makes sense
You know how covariance works, right?
Like, how tuple[Cat, ...] is a subtype of tuple[Animal, ...]
i think so, the tuple[type] relationship inherites from the normal one Cat(Animal)
I guess what is meant by "a function f is a subtype of another function g", is that if I have a fixed input object (say, a Cat), then there should be a polymorphic relationship between the functions; i.e., f is a subtype of g if and only if being able to pass my Cat to g implies that I can also pass it to f.
A type Child being a subtype of type Parent means that wherever you can pass Parent => you can also pass Child.
Let's take two functions:
get_animal_name: Animal -> strget_cat_name: Cat -> str
Now suppose that we have these two functions:
print_cat_names: (list[Cat], (Cat -> str)) -> Noneprint_animal_names: (list[Animal], (Animal -> str)) -> None
Can you pass get_cat_name into print_cat_names? Yes, you can -- it's the same type.
Can you pass get_animal_name into print_cat_names? Yes, you can -- if a function can print the name of an animal, it can print a name of a cat as well, because by definition you can pass a Cat wherever you can pass an Animal.
Can you pass get_animal_name into print_animal_names? Yes, you can -- it's the same type.
Can you pass get_cat_name into print_animal_names? No, you can't -- (Cat -> str) only knows how to get cat names, not names of arbitrary animals (i.e. passing Dog to it will fail)
So (Animal -> str) is a subtype of (Cat -> str), not the other way around.
What is a subtype to be clear?
Like a subclass, in that it supports all operations of its parent?
I think it means an is a relationship
A Cat is an Animal, and a function that accepts Animals is a function that accepts Cats.
By definition:
A type
Childbeing a subtype of typeParentmeans that wherever you can pass Parent => you can also passChild.
If you're talking about a language like Java, then subclassing or implementing an interface are the only ways to make a subtype.
In other languages, there are more ways to do that. For example, in Python int and str are both subtypes of Union[int, str].
it's important to distinguish between "type" and "class".
also that
types exist only at compile time, and can be taken as proofs/statements about a set of values
(I know this is kinda abstract)
The way I think of types are as interfaces in C# or Protocols in Python
you could say that type A is a subtype of type B if all the statements that hold for B hold also for A (I think this makes sense?)
yeah, that's the more "practical" way to think about it
there isn't really a difference between types and classes in something like Python
but to illustrate: the class of [] is list; it has only one class.
but it has a few types: minimally, list and object, and also stuff like Iterable, Sequence, etc.
So why is it sometimes the other way around?
What is the other way around?
@grave jolt do you think I made any misstatements
oh, Union is a great example of a type that isn't a class, too
Here? ^
ah okay i get it thank you for giving a full example, that was very helpful
now it makes sense why there's a word for this phenomenon because it is contra to the normal way of looking at it
Because it depends whether you are talking about functions into a space, or functions from a space.
Hi All.
I am developing a python wrapper and I wanna know what is the best design pattern should I follow, any hint?
more concretely, you can think of subtyping as "can this fit where that would fit?"
I am sorry but that did not clear things up, can you elaborate?
@gleaming rover you mean MVC?
idk I'm a noob
say there's a slot for a consumer of Animal (something that takes animals); I can't put in it a consumer of Cat, because it won't know how to handle Animals in general
@elder blade Does this clarify?
on the other hand, if there's a slot for a producer of Animal (something that makes animals), I can put in it a producer of Cat, because all Cats are always Animals
so whenever I need a Producer of Animal, I can always put in a Producer of Cat
but whenever I need a Consumer of Animal, I cannot put in a Consumer of Cat
on the other hand, wherever I need a Consumer of Cat, I can put in a Consumer of Animal
there were some good explanations of variance decisions in one of the typing peps
notice how the relationship is the other way round?
this is covariance (same direction), because Cat is an Animal, just like Producer<Cat> is a Producer<Animal>
and this is contravariance (opposite direction), because Cat is an Animal, but Consumer<Animal> is a Consumer<Cat>
Why not? Cats should support all things Animals does. So it should be able to handle Cats (though it won't use the Cat stuff)
Covariance, contravariance and invariance specify how the subtype relations of the inner types (type parameters) reflect on the outer types.
You can think of generic types (like list[T] ot (T -> str)) as functions from types to types. For example, if let f[T] = list[T], then f(int) == list[int], and if let g[T] = (T -> str), then g(Cat) == (Cat -> str).
Then, the variance of this function describes how a subtype relation of inputs influences subtype relations of outputs.
Let Child be a subtype of Parent .
if f is covariant, then f(Child) is a subtype of f(Parent)
if f is contravariant, then f(Parent) is a subtype of f(Child)
if f is invariant, then f(Parent) and f(Child) don't necessarily have any relation between them.
yes, but Animal doesn't support all the things Cat does
there may be Consumer<Cat>s that don't use any of the Cat-specific functionality, but that will not, in general, be true
Yeah I think I've read the 484 one, but they didn't really click
Suppose you have "holes" with types, like in this toy [Figure 1].
Suppose that you have a cat-shaped hole, a dog-shaped hole and an animal-shaped hole.
An animal-shaped hole is a subtype of cat-shaped hole because you can fit cats into it, you can fit dogs into it, etc.
However, a cat-shaped hole is not a subtype of an animal-shaped hole.
The argument of a function functions like a hole
okay, think about it this way.
all veggies are food, right? veggie is subtype of food, so you can use veggies wherever you need food.
but you cannot put a vegetarian wherever you need an omnivore, right? omnivore is subtype of vegetarian, because vegetarians are more "picky", so you can use an omnivore wherever you need a vegetarian.
Yeah I think I get that, the term "contravariant" makes sense.
I don't get how something becomes contravariant.
it depends on whether it's a producer or a consumer
if you produce something more specific than what you said you would, it doesn't harm anyone
but if you produce something more general, then they may not be equipped for that
Yup, that's variance. How will this work when things are contravariant?
on the other hand, if you consume something more general than what you said you would, again, it doesn't harm anyone
but if you consume something more specific (meaning you won't handle the other things), then someone may pass you something that wouldn't work for you
In this example, holes are contravariant. Hole[Animal] is a subtype of Hole[Cat], not the other way around
Hmm
In that sense, I guess it makes more sense. I've seen it from the perspective that it doesn't hurt if you take something more specific than you normally accept, because you simply won't use those extra stuff.
If X is a subtype of Y, I can fit X where I can fit Y?
Yes.
Right. So if I have f: Cat -> int that accepts Cats, and g: Animal -> int that accepts Animals, when we talk about whether g is a subtype of f, what we mean is: Can you substitute g for f? You've been thinking about substituting Cat for Animal, but we're talking about the functions themselves now.
A circle is a subtype of Y because a circle fits in a square?
it's the LISKOV SUBSTITUTION PRINCIPLE
A square is an animal, and a circle is a cat.
I can't fit a square in a circle hole
yes, in the case where you are an omnivore and you get a vegetable. but now what's happening is actually that we're putting out advertisements for omnivores and we get vegetarians.
square and circle are not subtypes of each other, so it's a bad example.
You can see the variance of a type with a trick - decompose the type as a tuple of functions
For example, you can represent tuple[T, ...] as these functions:
{
__getitem__: (int) -> T,
__getitem__: (slice) -> tuple[T, ...]
__len__: () -> int,
}
- The
__len__function doesn't depend onT, so let's ignore it - The
__getitem__function withints is contravariant - The
__getitem__function withslices has the same variance astuple - The only possible variance to assign here is covariant
Now let's decompose this object:
class Observer(Generic[T]):
def subscribe(self, fn: Callable[[T], None]) -> None: ...
def unsubscribe(self, fn: Callable[[T], None]) -> None: ...
Here, you can decompose Observer[T] as a pair of functions: ((T -> None), (T -> None)). They're both contravariant, so Observer itself can only be contravariant.
Actually geometric examples tend to be bad in general 🙂
btw can I just say that I really like type members and it makes me a bit sad that more languages don't have them
In Python, you can put a square into a circle hole -- you just put it sideways
or you carve the hole into a square on the fly
Why not? If I take a square and shave of the corners I get a circle, so a circle is a more specific square that fits in square holes
let us avoid geometric examples
🙏
I could also take the circle and shave bits of it off to get a square. This is the problem with geometric examples.
||What if the square shaves every square that doesn't shave itself||
oh no
Why are they contravariant?
Congrats, now your square has a .radius :\
Because functions are contravariant in their arguments ((Animal -> str) is a subtype of (Cat -> str))
I still don't really get it, but I won't waste your time. I'll look into it on my own.
Thank again for everything, this will make sense some day
😦
I think your main block is you're having a hard time imagining that functions themselves have a type
change channel name to type-theory-discussion
I understand that it's the case, but that's a bit too advanced for me to understand at the moment
Look at the room topic, if it fits go ahead. if you want help you should use our help channels.see #❓|how-to-get-help
Ok... Let me give it a shot. I am attempting to build a news aggregator using Django. Think Google News but for Cybersecurity. Essentially what I would like help with is the initial design to ensure that it is scalable. So, right now, I guess my question is how I should go about the design. I cant have the scraping scripts run every time the website loads. So how should I design this to make it scalable I guess... If thats too vague, let me know and I can clarify.
See #career-advice
apropos ? for optional stuff - what do you think of https://www.python.org/dev/peps/pep-0505/#unary-postfix-operator ?
You should ask on #web-development channel
oh im sorry...
Could work nicely with Optional in mypy
I don't really know honestly, i think it fits JavaScript with the style of syntax there and loads of object.data.properties.that.other.thing.finally as well as curly brackets.
I am not quite sure it's something I want added
@paper echo For "newbies" everything is pretty hard to learn
There's a few things that obviously could be improved, but overall I find logging well designed and very easy to understand
as a newbie I confirm that the way uvicorn treats logging is very hard to learn
uvicorn?
its the asgi fastapi uses
@pipes
def foobar():
return range(1,20) >> list
print(foobar())
🙂
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
PIPES!!!
i stole the code from https://github.com/robinhilliard/pipes and boiled it down to
class _PipeTransformer(ast.NodeTransformer):
def visit_BinOp(self, node):
if not isinstance(node.op, (ast.LShift, ast.RShift)):
return node
if not isinstance(node.right, ast.Call):
return self.visit(
ast.Call(
func=node.right,
args=[node.left],
keywords=[],
starargs=None,
kwargs=None,
lineno=node.right.lineno,
col_offset=node.right.col_offset,
)
)
node.right.args.insert(
0 if isinstance(node.op, ast.RShift) else len(node.right.args), node.left
)
return self.visit(node.right)
def pipes(func_or_class):
if isclass(func_or_class):
decorator_frame = stack()[1]
ctx = decorator_frame[0].f_locals
first_line_number = decorator_frame[2]
else:
ctx = func_or_class.__globals__
first_line_number = func_or_class.__code__.co_firstlineno
source = getsource(func_or_class)
tree = ast.parse(dedent(source))
ast.increment_lineno(tree, first_line_number - 1)
source_indent = sum(1 for _ in takewhile(str.isspace, source)) + 1
for node in ast.walk(tree):
if hasattr(node, "col_offset"):
node.col_offset += source_indent
tree.body[0].decorator_list = [
d
for d in tree.body[0].decorator_list
if isinstance(d, ast.Call)
and d.func.id != "pipes"
or isinstance(d, ast.Name)
and d.id != "pipes"
]
tree = _PipeTransformer().visit(tree)
code = compile(
tree, filename=(ctx["__file__"] if "__file__" in ctx else "repl"), mode="exec"
)
exec(code, ctx)
return ctx[tree.body[0].name]
it's really cool 🙂
makes code like ```python
contents = (
rec_path.read_text(encoding="UTF-8").strip().splitlines()
<< map(functools.partialmethod(str.partition, ",")._make_unbound_method())
<< map(operator.itemgetter(0))
>> list
)
wished i had that a year ago
i still wished we had built-in support for that with |> and |< as operators
or ```python
entry_path = (
contents_abs
<< map(
lambda c: c if any(str(c).rfind(str(e)) != -1 for e in entry_suffixes) else None
)
<< filter(None)
>> iter
>> next
)
filter(None) is so brutally simple and elegant
idk with the state of lambdas being what they are in python I'm not sure I prefer that over the regular python code
beyond which you have to live with the fact that it's not idiomatic, it's cute to send snippets around but if you do this in almost any team environment you're going to lose friends rather rapidly
entry_path = (c if any(str(c).rfind(str(e)) != -1 for e in entry_suffixes) else None for c in contents_abs) # this should really be more than one line as well
entry_path = filter(None, entry_path)
entry_path = next(iter(entry_path))
```not that much less clean, and much less nonstandard
redefining a variable isn't good style though
i think that strongly depends on the context. in data science you often end up chaining functions together, thus forcing you to break perfectly reasonable (and as pipe easily legible) code up and repeat things over and over (like @flat gazelle demonstrated)
with pipes like that, you can also swap out and delete lines with ease without requiring you to balance parenthesis
well, redefining a variable to a different type isn't good style, redefining a variable in general is fine. I agree I wouldn't do it there though.
If you have temporary variables you don't need to balance parentheses any more or less that I can see
it's just a lot of boilerplate that doesn't actually do anything
What it's mostly saving you is intermediate variables
also does that iter call actually do anything, doesn't seem like it should be necessary
oh, maybe next expects an iterator and not an iterable? I don't remember
I don't have anything against the style in general, to be clear, it's just that python has some things that make that style not feel very nice
You can't have anything that's more than one line or otherwise basically trivial, passed as function arguments here, so if you have something more complex it will need to eventually be factored out and stuffed into a function somewhere
if you have a for loop you can always add another line
It's true that in lakmatiol's version of the code, those intermediate variables feel pointless but on the flipside, the lambda passed to the first map call could actually benefit from some intermediate variables.
If it were a for loop, it would be trivial to refactor that one liner to have an intermediate variable, here it's not
here's the for loop version btw, as best I can understand:
def get_entry_path(contents_abs):
for c in contents_abs:
if any(str(c).rfind(str(e)) != -1 for e in entry_suffixes):
return c
raise RuntimeError("")
You can even do much better with generator comprehension; the original version decides to use a ternary if to None, and then filter right away, which is obviously kind of silly
entry_path = next(c for c in content_abs if any(str(c).rfind(str(e)) != -1 for e in entry_suffixes)
same scope shadowing is a fine way to handle transformations like this, though you do generally have to pick different names to make type checkers happy
idk, I'd disagree with that
same scope shadowing is at least widely viewed as kind of confusing, it's excluded from many languages, even in languages that have it (like Rust) I've seen discussions about it and I'm pretty sure there are warnings for people who want to opt out
in python there's no distinction between intiialization and assignment really, so pre-type checking, it wasn't an issue, and there was nothing to get confused about.
But with type checking coming into python, and the fact that it's not allowed in so so many languages, I think that's probably enough
instead of porting the overdone pipe version, building from the ground up..
next((c for c in contents_abs if any(str(c).rfind(str(e)) != -1 for e in entry_suffixes)))
inserting and filtering None is a red herring
I was assuming it was just to show a more elaborate example
of course you can rewrite it better
fair, would like to see an example of pipes solving a problem which would otherwise be complex
I think I can find sth in my old clojure projects
a
>> filter(lambda z: z == '.' or z in op or z.isdecimal())
>> partition_by(op)
>> map(''.join)
>> functools.reduce(a_really_weird_function, [])
>> filter(lambda a: a != 'inv') # used an atom in original code
>> ' '.join
>> lambda a: '`' + a + '`'
```is the most complex example I can find rn
[l.partition(",")[0] for l in rec_path.read_text(encoding="utf-8").strip().splitlines()] ?
there is a whole suite of tools python lacks to handle large expressions like this
exception handling is impossible once you started representing computation like this, as is proper resource management
and all of those can be fixed with more PEPs etc, but it is a lot of work, and I am not sure switching the conventional way to write code is a great thing to do this late in the language
actually, i can help with that. the first time i really, really wished for pipes in python was when i had some code in jupyter
filter(lambda z: z == '.' or z in op or z.isdecimal())
why not
filter(functools.partial(operator.eq, '.'))
wait what even is the iterable
because that is missing 2 of the conditional clauses
I know, but I got confused when I couldn't find the iterable
the pipes are an ast transformer which will inject the argument
that's the line where i wished for pipes:
semilogy(np.abs(signal.hilbert(np.abs(np.fft.fft(denoised)))))
one of many such lines
yeah, that's where python gets messy
I generally split these into multiple lines just retyping a variable name
if you want to add, delete a line, you always need to recount those damn paranthesis and you can't just swap out a function for another
with pipes it'd be much easier
here's a way this could be solved without adding syntax
pipe(denoised, np.fft.fft, np.abs, signal.hilbert, np.abs, semilogy)
yeah, I see no need for an ast transformer
this is not even a lot of work to implement
not a solution to the "swap out" argument
with a pipe you can just comment out a line without changing the rets
rest*
actually, it is
pipe(
denoised,
np.fft.fft,
np.abs,
signal.hilbert,
# np.abs,
semilogy,
)
```yeah, you can just do that
you are forgetting one very important, yet very annoying point in python
i already tried that approach and i fell flat on my nose
reason?
because python doesn't have this pipe-tradition, many functions don't work on the first parameter
you can just use lambdas
yeah, but you can't rely on "well, put it all in a list and iterate over it, it'll just work" approach
trust me, this is an issue in elm, haskell and clojure as well
it's the reason why two operators would be necessary, not just a single pipe operator
especially in clojure
i don't know about those, but i've had a stab at it in python before, and it didn't work like nop imagines it could
btw, there is the pipe package, which implements pipes much like you wrote them, but exactly for that reason i got very frustrated over it.. https://pypi.org/project/pipe/
i gave up on that quickly
!e
def pipe(data, *fns):
for fn in fns:
data = fn(data)
return data
pipe(
("foo", "bar"),
lambda d: tuple(s.capitalize() for s in d),
print,
)
@main lynx :white_check_mark: Your eval job has completed with return code 0.
('Foo', 'Bar')
and now with map
why?
because map, like many other functions, doesn't operate on the first argument
then why would you use it?
because it's useful?
when you've got a hammer, every problem looks like a nail? 😄
some things also look like a chisel
I have written a function like this before, it did sth like
def thread_last(data, *args):
for fn in args:
data = fn[0](*fn[1:], data) if isinstance(fn, tuple) else fn(data)
thread_last(
[1, 2, 3, 4, 5],
(map, lambda x: x * 4),
sum
)
```but it doesn't handle all cases, even in clojure, though clojure at least has really nice lambda syntax to make it easier to work around those functions
(->> '(1 2 3 4 5) (map #(* % 4)) (apply +))
```is quite a bit cleaner
@pipes
def foo():
((1,2,3,4,5)
<< map(lambda x: x * 4)
>> sum
>> print
)
something like that?
!e
def pipe(data, *fns):
for fn in fns:
data = fn(data)
return data
pipe(
(1, 2, 3, 4, 5),
lambda d: tuple(n * 4 for n in d),
sum,
print,
)
@main lynx :white_check_mark: Your eval job has completed with return code 0.
60
i find your lambda confusing
lambda d: tuple(n * 4 for n in d)
that's like quadruple the amount of complexity of my pipe
what if we looked at the implementation of your pipe?
I really don't think the few niche cases where you can't just write a helper of sorts to help with composition make it worthwhile to attach something this complex to any project I would want contributors for
that's why it should be python syntax :p
I don't think that high learning curve of pipes makes it worthwhile there either
and yes, there is a high learning curve
seems like it's also confusing if your background is c++ rather than other langs with this pipe syntax, works completely differently
do you want the different directions to pick the spot to insert the argument, rather than direction?
well, if you only want a single operator, you'd need to change a very large amount of python code..
(on a slightly related note, hey for syntax ambiguity)
a
>> f(2)
```can be either
```py
f(2, a)
```or
```py
f(2)(a)
i don't quite understand your question, tbh
true
if f(2) returns a callable
it is syntax in elm etc to have
1 |> fun
```and
```elm
fun <| 1
```mean the same thing
well, you can do it ~~both ~~either of the two ways
you can look at it as a polish notation of sorts
which way doesn't really matter, as long as it's consistent and you know where to look for input and output
i wouldn't allow both directions though, that's just confusing and people might try to mix it
but scripters are used to sh pipes which are from left to right, so do you just confuse half of the users?
I really can't imagine this fitting in anywhere, especially how minor of an improvement it is over helper functions
yeah, i know
well, ask data scientists how often they need to deal with chains of functions.. its applicability is quite obvious
yeah, I have seen the abominations that are large numpy expressions
and they are neither pure scripters or mathematicians 🙂
is there something wrong with writing it out over multiple lines?
yeah, I haven't ever really had an issue with just doing what I showed first, where you just set the variable multiple times or name all of the steps in more complex processes
i think newbies (i mean people who really have never programmed before, not people who don't use python) are scared by the complexity and verbosity of the setup. there's a tendency when learning something totally new for your brain to just "turn off" when you see a wall of unfamiliar stuff. a lot of my help sessions amount to forcing/encouraging/guiding the person through actually reading a docs page, error message, or source code
does this just monkeypatch __rshift__ onto functions?
it's like, say you write 10 lines of helper function to do a particular chain, now you unit test that helper function and move on, the complexity is abstracted away
99% of the R pipes i write get refactored out
oh neat this is just like how R pipes are implemented in Magrittr
it's also how you'd implement function composition like an adult, and threading macros, etc.
e.g. don't do this
def compose(f, g):
return h(*args, **kwargs):
return f(g(*args, **kwargs))
do this
class compose:
def __init__(self, *funcs):
self.funcs = funcs
def __call__(self, *args, **kwargs):
funcs = reversed(self.funcs)
result = funcs[0](*args, **kwargs)
for func in funcs[1:]:
result = func(result)
return result
i'd argue that refactoring something out isn't an argument for either side.. some things are nice to have for different situations (jupyter vs. module code, prototyping vs. production code etc)
yeah totally valid
i just wouldn't accuse data scientists of having any kind of enviable taste when it comes to programming
pipes are really cool
i love threading macros, pipes, etc.
🙂
but i actually think they are better in "app" development than in data science and scripting
how so?
when each function in the chain is tested, and you are probably using somewhat-powerful debugging and introspection tools, and you aren't constantly changing things
the other use case is when interacting with a REPL
the gray area in the middle is where pipes imo are not great
when you're iterating on code with non-trivial complexity and probably don't have a test suite
also in python specifically you don't have the kinds of meta-programming and/or first-class syntax support required to make pipes really effective
you basically need at least one of:
- syntactic macros (like R)
- currying (like Elixir)
- tidy anonymous function syntax (like a lot of languages that aren't python)
i think higher-order functions are really cool and stuff. but python has really poor support for higher-order programming and point-free style
well, the functional culture is missing so far that might make them easy to use them everywhere, but i'm optimistic that people would see the light when given the chance ^^
oh also >> is wack because you can't actually use it with code that uses rshift 🙂
not that you ever use rshift outside of cryptography and microcontroller stuff, but it's the principle of the thing
(maybe some binary protocol stuff)
not a problem with the code given since it's a decorator
i still think @ on callables should be function composition
literally __matmul__ on callables -> function composition
not sure about rmatmul, we had a discussion about it a while ago but never reached a satisfying conclusion
fg = f @ g
assert fg(x) == f(g(x))
@paper echo i personally think a tutorial is a lot more appropriate than a whole library
what, loguru?
i'd think numpy people would lynch you for that 😉
but ok, even accepting what you're saying, it still positions loguru as a library for... what, projects worked on exclusively by completely beginners?
that's not really a huge or compelling niche
very recently used rshift when converting to and from 8/24-bit color
i'm not advocating for loguru
i'm advocating for my PR https://bugs.python.org/issue45027
logging.basicConfig(level=logging.INFO, logger='app.__main__')
logging.basicConfig(level=logging.WARNING, logger='app._internal')
oh, but our whole convo started as I discussing around loguru as a solution to some of the complexity of logging I thought
I see
yeah, i think the desire to do something about it is legitimate
once i heard that loguru only uses the root logger i lost all interest
that's too far
oh, really?
someone here said that it did
Okay, so my "I'm not convinced the author understands the point of hierarchical logging" was 100% on point lol
i'll check for myself so i can stop spreading misinformation
The main concept of Loguru is that there is one and only one logger.
Yeah....
yes, you are 100% right and this is 100% misguided
this is literally for children and one-off scripts
yeah, pretty much
i.e. just use basicConfig
nobody should use this imo
i'm sure there are lots of useful utils and helper functions though
Yeah, I mean it seems harsh but when I saw the reddit post, and even looking at hte docs for loguru, it felt like kind of the work of a beginner
but you are right that the core idea is fundamentally misguided
like, a lot of very superficial improvements
if i need quick&dirty logging, i take q
q?
hehe
re the pipes and stuff, I've found that in many cases, when things get more complex, it's actually the for loop that ends up winning in terms of understanding what's going on
and even writing it
the syntax has the same energy as the perl q operator
I remember writing a python function for extracting include paths from a C++ compilation command, it was part of some of the YouCompleteMe scripts I was using
And trying really hard to do it with itertools and stuff like that
I was spending a decent amount of time on it, going back and forth on adding new missing fundamental itertools functions, etc...
then said to hell with it and had a for loop done in 2 minutes
yeah, I had a period where I was trying to be clever with tiny expressions, when the reality is that once you accept that you have to write the variable name multiple times etc, you get to use generators and get better code
Yeah. The thing is that in languages that support both styles equally, I still use a mix, I do often lean towards the more functional approach
the problem is that python does not support both styles equally
right, it has to do with what the language lends itself to
clojure for example has lots of little functions
python really pushes you fairly hard towards for loops for doing more complex things
python functions tend to be bigger and more imperative
if everything is 30-line functions with loops, a 5-liner of pipes is jarring mentally
yeah, I think it's the wrong direction to push, but python has enough other merits that I willing to overlook it
probably not unlike code switching in multilingual contexts
still want except ... try though
well, it's also jarring mentally because nobody else does it, and achieving "pipes" in python is full of horrible hacks
yeah
and writing lambda over and over is boilerplate too 🙂
but even a properly-designed thread-first operator would still be unnecessary abstraction in a typical python program
how so? it's literally just the code i posted earlier..
yes...
yeah, writing an ast transformer and using exec while replicating calling context is a hack.
pff. :p
in the same way using encodings to get arbitrary syntax is a hack
you could implement this
values = 'a a b c a b a c'.split()
n_unique = thread_first(values, set, len)
without any exec/ast bullshit, but would you?
in Kotlin, this kind of "piping" code is, approximately ten times nicer than python. No exaggeration.
And still sometimes I use for loops
@paper echowe did like 3 times over the course of this discussion
as discussed earlier, that's not really doable because.. yeah.
it is
but I used it once in real code and regret it
just write a variable name multiple times
it much more readable
yeah, a for loop also helps
One or the other, which one is more appropriate depends on the situation
no, because many python functions don't operate on the first argument and assuming so breaks things quite quickly and dirtily
you'd be surprised actually! but yeah that is one of the fundamental issues
partial and methodcaller help a lot
I kind of dislike partial to be honest
I should compile a list of things python would need to actually be good at large expressions
and I also dislike currying
if your lambdas are good enough none of those features offers you much mileage, IMHO
yeah, partial is nice.. @halcyon trail o.O?
there's also thread_last @verbal escarp
partial is nice in python, kind of, because python lambdas are horrible
What's horrible about them?
oh god ^^
the whitespace/indentation-based syntax fundamentally gets in the way of making them look and behave like def functions
pretty much, yeah
and yeah calling it lambda was a poor choice, should have been def or fn or something
they fail at both being concise and being expressive
I"m not sure what else there even is to fail at
if you can think of more criterion though they probably fail at those too
Can you assign a lambda to a variable? I forget. Maybe I tried and you can't.
you can
also, intentionally lacking some features
you can
although there is literally no reason to ever do that
!e ```python
def thread_first(value, *calls):
for call in calls:
if callable(call):
func = call
value = func(value)
elif len(call) == 2:
func, args = call
value = func(value, *args)
elif len(call) == 3:
func, args, kwargs = call
value = func(value, *args, **kwargs)
return value
values = 'a a b c a b a c'.split()
n_unique = thread_first(values, set, len)
print(n_unique)
@paper echo :white_check_mark: Your eval job has completed with return code 0.
3
because anonymous xD
lol
anyhow, you don't need
functools.partial(operator.eq, '.')
when you can just write
{ it == '.' }
omg, my eyes are tearing up when i look at this monstrosity
I have a lot more experience with Mathematica lambdas, but those are literally pattern matching rules, so they're very flexible.
is it a monstrosity? i think it's actually quite elegant
oh god, Mathematica
with type annotations it'd be pretty straightforward to understand
i also don't see how it solves the "not operate on first argument" issue
you'd have the equivalent thread_last
or use some very short lambdas
lambda is bearable imo if it's less than like 30 characters
yeah, pointfree composition is often not desirable
it's really more for looking smart than being good code in most cases I have seen
"pointfree" ?
indent_text = (
'\n'.join @
' {}'.format @
methodcaller('splitlines')
)
😎
no variables, just function calls to create a program
Functional programming is very elegant until it isn't.
"pointfree" actually means "pointless" duck and cover
I don't see an issue with chaining a few operations in a row. It really depends what they are, the context, etc.
even in small cases, this is just horrendous
(comp first (partial remove (partial > 0)) (juxt - identity) identity)
I don't know clojure so hard for me to comment
if you indent properly i don't hate it
the real issue is that function composition is "backwards"
so it's only good for mathematical-ish situations, which is where the idea comes from
Yeah. Can you explain what this does?
then I can rewrite it in a readable language 😉
lisps depend heavily on indentation for readability btw
one-liners in any lisp are generally awful
from toolz import compose, thread_first
def indent_line(text, n=4):
return '{}{}'.format(' '*n, text)
def indent_text_a(text, n=4):
return compose(
'\n'.join,
partial(map, partial(indent_line, n=n)),
methodcaller('splitlines')
)(text)
def indent_text_b(text, n=4):
return thread_first(
text,
methodcaller('splitlines'),
partial(map, partial(indent_line, n=n)),
'\n'.join
)
def indent_text_c(text, n=4):
return '\n'.join([indent_line(line, n=n) for line in text.splitlines()])
the other issue is that python comprehensions are great, and lest we forget are also a very "functional" construct
it's more or less
def juxt(*fs):
return lambda d: [f(d) for f in fs]
def identity(x):
return x
next(filter(lambda d: not (d > 0), juxt(operator.neg, identity)))
juxt is kind of wack for this
yeah, this is clearly a case of someone trying to be clever
like this particular code is kind of obfuscated, imo
i'd literally rather see a haskell arrow than this!
or some kind of other macro-based abstraction that juxt's everything together with the original value
but I have seen more examples of this than of pointfree code that was actually nicer (beyond omiting the last argument in haskell and other minor things)
this is a very extreme case though
most of what was being discussed was just performing multiple operations in a row on ordinary collections of numbers/strings, etc
def indent_text_a(text, n=4):
return compose(
'\n'.join,
partial(map, partial(indent_line, n=n)),
methodcaller('splitlines')
)(text)
this is actually the reason why i hate this approach to pipes
(that is function composition so the order is reversed for extra confusion)
exactly
so methodcaller goes first?
the input is on the bottom
at this point I am more used to function composition order than shell order tbh
i've written text cleaning functions that looked like this
def clean_text(t):
t = clean1(t)
t = clean2(t)
t = clean3(t)
t = clean4(t)
return 4
and i explicitly didn't use pipes. you know why? because you can't step through a pipe or threading macro in your repl!!
ugh, and calling a method by passing a string literal
that's awful
you can't get any help from the IDE or any static analysis at that point
yeah methodcaller i guess was supposed to paper over lambda but it's super verbose
the entire operator module is like that
!d operator
Source code: Lib/operator.py
The operator module exports a set of efficient functions corresponding to the intrinsic operators of Python. For example, operator.add(x, y) is equivalent to the expression x+y. Many function names are those used for special methods, without the double underscores. For backward compatibility, many of these have a variant with the double underscores kept. The variants without the double underscores are preferred for clarity.
The functions fall into categories that perform object comparisons, logical operations, mathematical operations and sequence operations.
The object comparison functions are useful for all objects, and are named after the rich comparison operators they support:
the thing is that these things all look pretty bad in python
btw we are kind of implicitly ragging on the toolz module but it's really a great library
!pypi toolz
you could get that kind of support if it was built into the language though
def indent_text_a(text, n=4):
return compose(
'\n'.join,
partial(map, partial(indent_line, n=n)),
methodcaller('splitlines')
)(text)
In kotlin, which actually has proper support for this kind of style, this becomes
fun indentText(text: String, n: Integer = 4): String = text
.lines()
.map { indent_line(it, n=n) }
.joinToString('\n')
Which I think is perfectly readable, without any intermediate variables
it doesn't have to be a standalone hack
i agree quicknir
with this left to right style as well, the IDE also keeps track of the static type at each step in the transformation, so you always get auto completion at every step
instead of starting on the last operation, and the IDE obviously can't help you since it doesn't know what you plan to operate on yet
I did advent of code in Kotlin this past year and I have a lot of snippets just like the one above, I was looking for the best one to show out o fcontext but then just figured it was easier to convert an existing example
well in haskell the function composition is well-typed
yeah, my point isn't so much about it being well typed. my point is that because of teh combination of that and other things (for example extensive use of extension functions), you get massive amounts of actual help from the IDE
apropos left to right vs right to left - i just noticed that if you have a composition on one line and want to split it into multiple lines, with left to right, you only need to hit enter a couple of times basically and get a nice top to bottom listing, which is the (for this undisputed) correct direction
ask in #python-discussion
(short answer: it's a big C program)
(long answer: CPython is a big C program 🙂 )
what cpython is? i heard smth abt it.
Python interpreter written in C, hence, CPython.
what the main difference between python and cpython ??
Python is a language. It's an abstract thing that exists on its own (just like English language or French language). CPython is a particular implementation of the language (like a speaker that can understand a language). It's a program that reads Python code as text and executes it.
cpython is the interpreter??
it's an interpreter
ic
There are other ones, like PyPy, which has some different properties
mypy
mypy is just a tool for type checking, it's not an interpreter
jython is one
It's an implementation of (an old version of) the Python language. Others include IronPython, MicroPython, CircuitPython, pycopy, and arguably cython.
Some of them are dialects (like micropython or cython), others are the same language but with different properties (e.g. PyPy has a JIT compiler, which often makes it faster)
it's still python, all the syntax is the same, the behaviors are all the same (at least they should be)
ic
ideally anything you'd call "python" would behave the same for the user beyond implementation details
how do you figure micropython is a dialect?
i got it
what does dialect even mean in this context?
doesn't micropython take out some core features?
Cython is arguably a dialect, in that it is a superset of Python. It interprets Python, plus some other stuff.
Some very minor ones - like you can't do 1or 2 like you can in Python.
like theres some benefits to use cpython instead python interpreter?
CPython is the Python interpreter. It's the normal one.
so when i type on the terminal
python file.py
im using cpython?
I think some of these https://docs.micropython.org/en/latest/genrst/index.html would technically not make it a proper implementation of python, but it's mostly semantics with minor differences like that
!e ```py
import sys
print(sys.implementation)
@raven ridge :white_check_mark: Your eval job has completed with return code 0.
namespace(name='cpython', cache_tag='cpython-39', version=sys.version_info(major=3, minor=9, micro=6, releaselevel='final', serial=0), hexversion=50923248, _multiarch='x86_64-linux-gnu')
^ The name of the implementation of the Python interpreter is "cpython".
around 20 years, I guess.
i completed one year last month lol
what is the best way to practice programing?
games i think...
it is so logicals
!projects has some suggestions.
Kindling Projects
The Kindling projects page on Ned Batchelder's website contains a list of projects and ideas programmers can tackle to build their skills and knowledge.
It has some inconsistencies with the "normal" Python - some modules are missing (AFAIK), there are some non-standard modules, and there are some very minor differences in syntax http://docs.micropython.org/en/latest/genrst/syntax.html
I guess the minor syntax differences are enough
it's missing most of the standard library, but I wouldn't say that makes it a dialect, because the standard library isn't part of the language
i made a multiplayer game some days ago
some of those syntax differences are bugs in CPython that are getting fixed 😄
!e print(1or 2)
@raven ridge :white_check_mark: Your eval job has completed with return code 0.
1
lol
nooooooooo fixing that would break so many golfs
It's quite hard to read syntatically, so it'll raise a deprecation warning now.
!e print(1 and 2)
@candid pelican :white_check_mark: Your eval job has completed with return code 0.
2
why it happens
https://docs.python.org/3.10/whatsnew/3.10.html#deprecated Deprecated in 3.10, will be removed eventually.
the output shouldn't be 12
or returns the left argument if it's truthy, otherwise it returns the right argument.
and returns the left argument if it's falsy, otherwise it returns the right argument.
ic
neither of them even evaluate the right argument unless they need to.
!e 1 or print("hello")
@raven ridge :warning: Your eval job has completed with return code 0.
[No output]
!e 0 and print("hello")
@raven ridge :warning: Your eval job has completed with return code 0.
[No output]
!e 1 and print("hello")
@raven ridge :white_check_mark: Your eval job has completed with return code 0.
hello
like a ternary operator
!e print("nope") if False else print("yep")
@raven ridge :white_check_mark: Your eval job has completed with return code 0.
yep
generalist generally gives you more opportunities.
I wouldn't specialize until later in your career.
i buy it
i'm 15 it's kinda confusing lol
in 3 years i gonna be an adult lol
how do you study programming??
the same way you study anything. read about it, practice it, watch videos, whatever you do to study
mhm
experiment, largely.
you learn to draw by making a lot of shitty drawings. You learn to program by making a lot of shitty programs.
good one lol
👋
!e ```python
from timeit import repeat
setup = 'a = [0] * 10000'
expressions = [
'any(x % 2 for x in a)',
'any(True for x in a if x % 2)',
]
for expression in expressions:
times = sorted(repeat(expression, setup, number=1))
print(*(f'{t:0.4f}' for t in times), expression)
@paper echo :white_check_mark: Your eval job has completed with return code 0.
001 | 0.0009 0.0009 0.0009 0.0010 0.0010 any(x % 2 for x in a)
002 | 0.0005 0.0005 0.0006 0.0006 0.0006 any(True for x in a if x % 2)
the explanation is interesting... the if-based filtering happens "internally", so any only ever sees a single value of True and then exits
!e ```python
from timeit import repeat
setup = 'a = [0] * 100000'
expressions = [
'any(x % 2 for x in a)',
'any(True for x in a if x % 2)',
]
for expression in expressions:
times = sorted(repeat(expression, setup, number=1))
print(*(f'{t:0.4f}s' for t in times), expression)
@paper echo :white_check_mark: Your eval job has completed with return code 0.
001 | 0.0088s 0.0089s 0.0089s 0.0090s 0.0094s any(x % 2 for x in a)
002 | 0.0055s 0.0055s 0.0059s 0.0059s 0.0060s any(True for x in a if x % 2)
👋 ❓
to continue the analogy, I'm a big shitty picture, like a nanopoop smeared across a stadium
conditional iter comprehensions are how to python fast, it's true
it's a tighter loop to not yield up to the any
wait so if I'm understanding correctly, the comprehension and the any function need to yield back to each other for values that aren't explicitly booleans, otherwise it doesn't need ot do that?
in the first case the loop is 1) iter comprehension yields a numbers 2) any checks number for truthiness (non-zero) 3) if True, return True
in the second case the loop is 1) iter comprehension yields True if outcome is truthy 2) any returns True if it was yielded
so in the second case the entire iteration is hidden from any until it can return, it only sees either 0 or 1 value from the iter
Ahh
since it's explicitly a boolean, it doesn't need to yield back to all
any*
until it's done
there is some overhead to yielding a value from the iter, so doing it up to 100k times doesn't scale as well as doing it 0 or 1 times
it's not even about the value being a boolean, more about how many times it yields
if i fold 100k origami cranes and pass each one to you for inspection before i start the next one, it's a little slower than me just inspecting them myself
and you just waiting for me to hand you a good one
so does any have to evaluate truthy values and therefore the iter has to yield back to any? if it isn't a boolean
it's that the if in the list comp does the same work as any, but without the overhead of bouncing back and forth yielding stuff
im dying for these python speed ups for 3.11
@surreal sun any is pretty much this:
def any(items):
for value in item:
if bool(value):
return True
return False
(the bool() is unnecessary but i added it for clarity)
idk if anyone has been following and has an idea of the speed ups
3.11 has significant speedups planned?
i'm curious what those might be. my impression of python is that it's actually pretty well optimized, but that there are fundamental limitations on how fast it can go without removing some of the more esoteric features and breaking backward compatibility w/ some big libraries
Yes. Microsoft is hosting it
So without the if and the True if [whatever], it has to yield back to any to tell whether or not the value is truthy
Mark Shannon was hired to work with GvR at microsoft. They had hirings for it earlier in the yera
looks like they want to target the bytecode since it doesn't need to be compatible across versions
