#internals-and-peps

1 messages · Page 129 of 1

elder blade
#

Typhon incoming

grave jolt
#

I was thinking of making TypeScript-but-in-Python with a reworked type system, but I'm too lazy and incompetent

static panther
#

Typethon

nova iris
#

isn't this advanced discussion, not esoteric python lol

pliant tusk
floral quiver
#

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 goodmorning

nova iris
#
>>> (-1)**(1/2)
(6.123233995736766e-17+1j)
```what
#

is this fpa weird crap or did i do something wrong

charred pilot
#

the real part is very close to zero, probably just rounding issues

flat gazelle
#

yeah, this is another case of floats being inaccurate

nova iris
#

@charred pilot @flat gazelle i agree, i tried using cmath.sqrt and it gave me a clean and precise 1j

stark reef
#

whereabouts would I go to ask Celery questions?

nova iris
#

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)

peak spoke
nova iris
peak spoke
#

I think you can create an account through github on the left, then just create a new issue with the doc component

nova iris
#

thanks again!

verbal escarp
true ridge
#

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

verbal escarp
#

nice!

true ridge
#

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.

fallen slateBOT
#

Parser/string_parser.c line 277

// FSTRING STUFF```
true ridge
#

you could simply parse/eval it by adding "f" infront of the string's repr().

elder blade
verbal escarp
#

there's nothing "simple" there

verbal escarp
elder blade
pliant tusk
#

!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))```

fallen slateBOT
#

@pliant tusk :white_check_mark: Your eval job has completed with return code 0.

The tournament 'One' is in teams of 3
pliant tusk
#

^ a quick and dirty implementation where it just uses eval

verbal escarp
#

lol ^^

#

the answer to the original question, nice

main ginkgo
#

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

unkempt rock
#

How would you make a script that can do grammar jacks for you?

#

in roblox for army groups

prime estuary
static bluff
#

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.

raven ridge
#

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.

static bluff
#

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.

candid pelican
#

@summer forge you could use encapsulation...

class Foo:
  __private: None
  _protected: None
  public: None
grave jolt
candid pelican
#

yes but is kinda "hard" to access....

#

as you just said...

unkempt rock
#

hi

#

I'm new

grave jolt
#

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

candid pelican
#

ic

#

hi @somber geode

valid rose
#

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

gleaming rover
#

constructing lists from them, for example

valid rose
#

since its not guaranteed to be accurate, how is it used?

pliant tusk
#

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

fallen slateBOT
#

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);
    }
}```
fallen slateBOT
#

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;```
pliant tusk
#

so tuple(v) calls PySequence_Tuple(v) which allocates based on __length_hint__ @valid rose ^

main ginkgo
#

used in list.extend too

verbal escarp
#

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"

grave jolt
verbal escarp
#

calls security :p

sick schooner
#

print(len("ISGAYME"))

verbal escarp
#

?

unkempt rock
#

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

verbal escarp
#

blender?

#

it has python bindings afaik

elder blade
#

Blender has a Python language you can use

cerulean hill
#

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

charred pilot
#

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

sturdy timber
#

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)

grave jolt
#

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)
sturdy timber
grave jolt
#

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

sturdy timber
#

Probably Option is better than Options

grave jolt
#

I'd just go with a flat structure

elder blade
peak spoke
#

No, in the class body they're not yet namespaced

elder blade
#

Oh wait yeah, the union is defined inside the class

sturdy timber
# grave jolt I'd just go with a flat structure

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.

grave jolt
#

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)

elder blade
sturdy timber
# elder blade 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.

paper echo
charred pilot
#

named tuple?

paper echo
#

meh

peak spoke
#

modules?

paper echo
#

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

peak spoke
#

How would the usage look?

grave jolt
#
module Option:
    ...
paper echo
#

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
grave jolt
paper echo
grave jolt
#

but there's not much tool support for it

#

and you can't do class, def etc. inside of it

paper echo
#

that seems fine in this case though

grave jolt
#

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)
fallen slateBOT
#

@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)
grave jolt
#

this is pure evil

paper echo
#

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

grave jolt
#

yeah lol, that's the whole thing

#

I wonder if I can write a mypy plugin for this

#

sounds hard

paper echo
#

!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)
fallen slateBOT
#

@paper echo :white_check_mark: Your eval job has completed with return code 0.

<class '__main__.options.Number'>
paper echo
#

i think i would legitimately use this

#

not sure if it's possible to make it prettier than metaclass=Namespace though

grave jolt
#
class NamespaceMeta(type):
    def __new__(meta, name, bases, attributes):
        return SimpleNamespace(**attributes)

class Namespace(metaclass=NamespaceMeta):
    pass

class options(Namespace):
    ...
paper echo
#

ah i wasn't sure if that would work, not sure what i was afraid of

paper echo
#

maybe Namespace should also implement __subclasshook__ and/or __instancecheck__?

grave jolt
#

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?

grave jolt
fallen slateBOT
#

@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)
surreal sun
paper echo
#

unless you use that to wrap every function in staticmethod()

grave jolt
#

I feel every muscle on that smiley's face

paper echo
#

!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)
fallen slateBOT
#

@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
paper echo
#

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

fallen slateBOT
#

@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'>
nova iris
#

woohoo! the pr has been merged!!

#

__div__ is no more

surreal sun
#

I swear I only remember using truediv

nova iris
# surreal sun Who even used div?

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

woven tusk
#

ok new q does anyone have experience with priority queues

#

lol

nova iris
surreal sun
#

Ah

#

That’s cool

nova iris
#

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!

surreal sun
#

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

nova iris
nova iris
#

it's natural lol

surreal sun
#

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

nova iris
#

flashbacks to the decorator days
that really blew my mind, seeing functions can be inputs and outputs to other functions

surreal sun
#

Decorators really stumped me lmao

nova iris
#

actually, i believe there's a study on this

surreal sun
#

Dunning Kruger effect?

#

I wish I could understand open source contributions

nova iris
#

google search time

surreal sun
#

I’m usually not good at contributing if I don’t know most of the codebAse

nova iris
#

LMFAO WAIT HOW IS THIS SO DAMN ACCURATE

#

HOW

surreal sun
#

Not for me cause I already knew the effect when I started learning

nova iris
#

lol nice

#

learned something new today, this is amazing

surreal sun
#

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

nova iris
#

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

surreal sun
#

This is my favorite representation of the dinning Kruger effect

nova iris
#

reminds me of the uncanny valley

unkempt rock
#

why he don't need work

paper echo
#

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

nova iris
#

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

elder blade
nova iris
#

i used to think that 2 + 3j was ONE THING

#

like, python recognizes it as one atom

elder blade
#

Oh haha, no it follows the rules of prioritisation in math. So first 3 * j then 2 + result

flat gazelle
#

!e
well, it is not quite that simple

from ast import literal_eval
print(literal_eval('1+1j'))
fallen slateBOT
#

@flat gazelle :white_check_mark: Your eval job has completed with return code 0.

(1+1j)
flat gazelle
#

or well, it is, literal_eval just is weird

peak spoke
#

would be weird if that or -1 didn't work

grave jolt
#

!e

from ast import literal_eval
print(literal_eval("set()"))
fallen slateBOT
#

@grave jolt :white_check_mark: Your eval job has completed with return code 0.

set()
grave jolt
#

o

#

i c

flat gazelle
#

yeah, it seems to special case some things

verbal escarp
#

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()

paper echo
#

!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()

fallen slateBOT
#

@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
paper echo
#

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()

fallen slateBOT
#

@paper echo :white_check_mark: Your eval job has completed with return code 0.

INFO:__main__:testing
paper echo
#

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?

verbal escarp
#

yeah, how those modules come to live it's one of the weirdest things i've ever seen in python

paper echo
#

heh

verbal escarp
#

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!"

paper echo
#

yeah you basically exec the code with the module temporarily set to the global namespace right?

verbal escarp
#

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

pliant tusk
verbal escarp
#

weirdest bug ever

paper echo
#

i just saw that

#

and __qualname__ too

nova iris
#
>>> print("\u00C7")
Ç
>>> print("\u0043\u0327")
Ç
>>> "Ç" == "Ç"
False
```unicode codepoint confusion lol
verbal escarp
#

yeah, exactly my reaction when i saw that

verbal escarp
nova iris
#

this would be super fun to use to troll people ducky_devil

#

the cyrillics are a method of the past.

#

embrace: precomposed vs composing unicode!

paper echo
#

my standard "data scientist who doesn't trust anyone with data" text cleaning protocol always started with unicodedata.normalize and str.casefold

nova iris
#

lmao

nova iris
#

i've known about str.casefold, but unicodedata.normalize is cool

nova iris
#

.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

paper echo
#

is .bm broken?

nova iris
# paper echo 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

nova iris
paper echo
#

!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__)
fallen slateBOT
#

@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
peak spoke
#

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
paper echo
#

ah, so you'd have to wrap the function to invoke this context manager before every call

cloud crypt
#

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

elder blade
#

It's just a wrapper over logging, I think it's cool

flat gazelle
#

Ye, it seems nicer than logging

paper echo
#

it's been on my to-investigate list

halcyon trail
#

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

paper echo
#

i've said before that logging.basicConfig just needs a logger= kwarg so you can specify what logger to operate on

halcyon trail
#

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

nova iris
#

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

charred pilot
#

by "identical" there, it means an is check?

nova iris
#

[...] 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.

surreal sun
#

I remember seeing something about how the statistics module can take in float("nan") and then is ignored

nova iris
#

lmao why python- stop your idealistic reflexivity dreams

#

you ruined your dreamscape with nan. face the consequences.

surreal sun
#

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

peak spoke
#

floats are done in C, you have to look at the source on github

surreal sun
#

ah

#

Thanks

#

well float("nan") is truthy, so that might mean something? idk honestly lmao

nova iris
# surreal sun it might give an explanation as to why nan works that way

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

peak spoke
#

I'd guess they just use normal C comparisons on the underlying floats

nova iris
#

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

nova iris
surreal sun
#

!e

[[1, 2]] == [[1, 2]]
>>> False
fallen slateBOT
#

@surreal sun :x: Your eval job has completed with return code 1.

001 |   File "<string>", line 2
002 |     >>> False
003 |     ^
004 | SyntaxError: invalid syntax
surreal sun
#

!e ```py
print([[1, 2]] == [[1, 2]])

fallen slateBOT
#

@surreal sun :warning: Your eval job has completed with return code 0.

[No output]
surreal sun
#

!e ```py
print([[1, 2]] == [[1, 2]])

fallen slateBOT
#

@surreal sun :white_check_mark: Your eval job has completed with return code 0.

True
nova iris
surreal sun
#

ty

nova iris
#

🇳🇵

halcyon galleon
#

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 rooDerp

peak spoke
#

makes your annotations act like forwardrefs implicitly

nova iris
halcyon galleon
#

__future__ is a real module
Official docs starting off great making me feel comfy. rooC

nova iris
#

i always use def f(self) -> "A" instead

peak spoke
halcyon galleon
#

Interdasting. I didn't know you could use a literal in place

nova iris
#

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

surreal sun
#

!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"

fallen slateBOT
#

@surreal sun :white_check_mark: Your eval job has completed with return code 0.

001 | __name__
002 | __name__
nova iris
#

ah, the classic weird namespace + __prepare__ + __init_subclass__ combo

#

actually wait, you don't need __init_subclass__ there?

surreal sun
#

yeah i don't think so

#

let me remove it

nova iris
#

you aren't passing in any **kwargs

surreal sun
#

!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
fallen slateBOT
#

@surreal sun :white_check_mark: Your eval job has completed with return code 0.

001 | __name__
002 | x
surreal sun
#

nice, it works

nova iris
#

metaclass fun

#

next up: metametaclass

#

actually, i wonder if an actual metametaclass has ever been used in production code

#

hey, fun fact everybody

paper echo
#

that's 20 lines of boilerplate to copy and paste into every project

nova iris
#

lol, this is cool

surreal sun
#

!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)
fallen slateBOT
#

@surreal sun :white_check_mark: Your eval job has completed with return code 0.

5
nova iris
#

what is this cursed insanity

surreal sun
#

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

nova iris
#

"not that cursed"
meanwhile, literally uses __annotations__ to set values-

nova iris
#

oh wait nvm

#

me dumb

#

class_setitem doesn't exist

#

only class_getitem does

surreal sun
candid pelican
#

the lambda decorator does not works....

#

syntax error

nova iris
candid pelican
#

in the google colab yes, it does.

#

look it.

peak spoke
#

arbitrary expressions in decorators is new to 3.9

candid pelican
#

ic...

#

lemme check on the vscode

surreal sun
peak spoke
#

it just calls the result of that expression

surreal sun
#

ah

#

like a normal decorat

#

or

candid pelican
#

tbh why should i use that??

surreal sun
# candid pelican 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

candid pelican
#

mhm ic

fallen slateBOT
#

@unkempt rock :white_check_mark: Your eval job has completed with return code 0.

001 | <class '__main__.A'>
002 | <__main__.B object at 0x7f26f7d63fd0>
peak spoke
candid pelican
#

lmao

paper echo
candid pelican
surreal sun
#

For most use cases you don't wanna use it

paper echo
#

!eval ```python
@3
def f():
return

fallen slateBOT
#

@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
surreal sun
#

You don't want to convert your class to an object

#

at the beginning

candid pelican
#

but i can just call the class

peak spoke
candid pelican
unkempt rock
#

what's your favorite static type checker

surreal sun
#

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

unkempt rock
#

they couldnt handle the walrus

warped ocean
#

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?

acoustic crater
#
__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

unkempt rock
#

holy shit generics are the coolest thing

gleaming rover
quaint pike
#

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)

flat gazelle
#

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.

elder blade
#

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?

quaint pike
#

Huh, I didn't realize you could give functions attributes

#

What's an actual use case of that?

gleaming rover
quaint pike
#

ah, interesting

lament sinew
#

i kinda hate __call__ because you can't git grep where it's called

flat gazelle
#

yeah, it is generally much better (even just for readability once it gets passed into more functions) to do

obj.method_as_a_callable
grave jolt
#

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

gleaming rover
#

I quite like the abstraction

unkempt rock
#

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.
pithink ... what

eternal scroll
#

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')
grave jolt
unkempt rock
grave jolt
#

Like, how tuple[Cat, ...] is a subtype of tuple[Animal, ...]

unkempt rock
quaint pike
#

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.

grave jolt
# unkempt rock thanks ya im having trouble understanding the wiki explaining contravariance. ...

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:

  1. get_animal_name: Animal -> str
  2. get_cat_name: Cat -> str

Now suppose that we have these two functions:

  1. print_cat_names: (list[Cat], (Cat -> str)) -> None
  2. print_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.

elder blade
#

What is a subtype to be clear?

#

Like a subclass, in that it supports all operations of its parent?

quaint pike
#

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.

grave jolt
# elder blade Like a subclass, in that it supports all operations of its parent?

By definition:

A type Child being a subtype of type Parent means that wherever you can pass Parent => you can also pass Child.

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].

gleaming rover
grave jolt
#

also that

gleaming rover
#

types exist only at compile time, and can be taken as proofs/statements about a set of values

#

(I know this is kinda abstract)

elder blade
#

The way I think of types are as interfaces in C# or Protocols in Python

gleaming rover
#

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?)

gleaming rover
#

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.

elder blade
grave jolt
gleaming rover
#

@grave jolt do you think I made any misstatements

#

oh, Union is a great example of a type that isn't a class, too

unkempt rock
quaint pike
austere rapids
#

Hi All.

I am developing a python wrapper and I wanna know what is the best design pattern should I follow, any hint?

gleaming rover
elder blade
austere rapids
#

@gleaming rover you mean MVC?

gleaming rover
#

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

quaint pike
gleaming rover
#

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

gleaming rover
gleaming rover
#

on the other hand, wherever I need a Consumer of Cat, I can put in a Consumer of Animal

paper echo
#

there were some good explanations of variance decisions in one of the typing peps

gleaming rover
#

notice how the relationship is the other way round?

gleaming rover
gleaming rover
elder blade
grave jolt
# elder blade Here? ^

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.

gleaming rover
#

there may be Consumer<Cat>s that don't use any of the Cat-specific functionality, but that will not, in general, be true

elder blade
grave jolt
#

The argument of a function functions like a hole

gleaming rover
elder blade
gleaming rover
#

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

elder blade
gleaming rover
#

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

grave jolt
elder blade
elder blade
grave jolt
#

Yes.

quaint pike
elder blade
#

A circle is a subtype of Y because a circle fits in a square?

boreal umbra
elder blade
gleaming rover
quaint pike
#

square and circle are not subtypes of each other, so it's a bad example.

grave jolt
# elder blade Yup, that's variance. How will this work when things are contravariant?

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,
}
  1. The __len__ function doesn't depend on T, so let's ignore it
  2. The __getitem__ function with ints is contravariant
  3. The __getitem__ function with slices has the same variance as tuple
  4. 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.

quaint pike
#

Actually geometric examples tend to be bad in general 🙂

gleaming rover
#

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

grave jolt
#

In Python, you can put a square into a circle hole -- you just put it sideways

gleaming rover
elder blade
gleaming rover
#

🙏

quaint pike
grave jolt
#

||What if the square shaves every square that doesn't shave itself||

quaint pike
#

oh no

quaint pike
grave jolt
elder blade
#

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

grave jolt
#

😦

quaint pike
#

I think your main block is you're having a hard time imagining that functions themselves have a type

charred pilot
#

change channel name to type-theory-discussion

elder blade
lean basin
#

Is anyone here?

#

Its actually an advanced question...

visual shadow
#

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

lean basin
#

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.

cyan scarab
verbal escarp
grand shore
candid pelican
#

oh im sorry...

brave badger
elder blade
halcyon trail
#

@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

grave jolt
#

as a newbie I confirm that the way uvicorn treats logging is very hard to learn

halcyon trail
#

uvicorn?

astral gazelle
#

its the asgi fastapi uses

verbal escarp
#
@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!!!

#
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

halcyon trail
#

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

flat gazelle
#
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
verbal escarp
#

redefining a variable isn't good style though

verbal escarp
#

with pipes like that, you can also swap out and delete lines with ease without requiring you to balance parenthesis

halcyon trail
#

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

verbal escarp
#

it's just a lot of boilerplate that doesn't actually do anything

halcyon trail
#

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)
flat gazelle
#

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

halcyon trail
#

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

main lynx
#

inserting and filtering None is a red herring

flat gazelle
#

I was assuming it was just to show a more elaborate example

#

of course you can rewrite it better

main lynx
#

fair, would like to see an example of pipes solving a problem which would otherwise be complex

flat gazelle
#

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
main lynx
flat gazelle
#

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

verbal escarp
boreal umbra
#
filter(lambda z: z == '.' or z in op or z.isdecimal())

why not

filter(functools.partial(operator.eq, '.'))

wait what even is the iterable

flat gazelle
#

because that is missing 2 of the conditional clauses

boreal umbra
#

I know, but I got confused when I couldn't find the iterable

flat gazelle
#

the pipes are an ast transformer which will inject the argument

verbal escarp
#

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

flat gazelle
#

yeah, that's where python gets messy

#

I generally split these into multiple lines just retyping a variable name

verbal escarp
#

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

main lynx
#

here's a way this could be solved without adding syntax
pipe(denoised, np.fft.fft, np.abs, signal.hilbert, np.abs, semilogy)

flat gazelle
#

yeah, I see no need for an ast transformer

main lynx
#

this is not even a lot of work to implement

verbal escarp
#

not a solution to the "swap out" argument

#

with a pipe you can just comment out a line without changing the rets

#

rest*

flat gazelle
#
pipe(
  denoised,
  np.fft.fft,
  np.abs,
  signal.hilbert,
#  np.abs,
  semilogy,
)
```yeah, you can just do that
verbal escarp
#

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

flat gazelle
#

you can just use lambdas

verbal escarp
#

yeah, but you can't rely on "well, put it all in a list and iterate over it, it'll just work" approach

flat gazelle
#

trust me, this is an issue in elm, haskell and clojure as well

verbal escarp
#

it's the reason why two operators would be necessary, not just a single pipe operator

flat gazelle
#

especially in clojure

verbal escarp
#

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

main lynx
#

!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,
)
fallen slateBOT
#

@main lynx :white_check_mark: Your eval job has completed with return code 0.

('Foo', 'Bar')
verbal escarp
main lynx
#

why?

verbal escarp
#

because map, like many other functions, doesn't operate on the first argument

main lynx
#

then why would you use it?

verbal escarp
#

because it's useful?

main lynx
#

when you've got a hammer, every problem looks like a nail? 😄

verbal escarp
#

some things also look like a chisel

flat gazelle
#

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
verbal escarp
#
@pipes
def foo():
    ((1,2,3,4,5)
    << map(lambda x: x * 4)
    >> sum
    >> print
    )
#

something like that?

main lynx
#

!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,
)
fallen slateBOT
#

@main lynx :white_check_mark: Your eval job has completed with return code 0.

60
verbal escarp
#

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

main lynx
#

what if we looked at the implementation of your pipe?

verbal escarp
#

sure

#

that's actually running code now

#

(given the initially posted code)

flat gazelle
#

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

verbal escarp
#

that's why it should be python syntax :p

flat gazelle
#

I don't think that high learning curve of pipes makes it worthwhile there either

#

and yes, there is a high learning curve

main lynx
#

seems like it's also confusing if your background is c++ rather than other langs with this pipe syntax, works completely differently

verbal escarp
#

i would rather suggest a syntax like |> and |<, yes

#

but that's just me

flat gazelle
#

do you want the different directions to pick the spot to insert the argument, rather than direction?

verbal escarp
#

well, if you only want a single operator, you'd need to change a very large amount of python code..

flat gazelle
#

(on a slightly related note, hey for syntax ambiguity)

#
a
>> f(2)
```can be either
```py
f(2, a)
```or 
```py
f(2)(a)
verbal escarp
verbal escarp
#

if f(2) returns a callable

flat gazelle
#

it is syntax in elm etc to have

1 |> fun
```and
```elm
fun <| 1
```mean the same thing
verbal escarp
#

ah, direction of reading

#

i'd always go from left to right

flat gazelle
#

mathematicians go from right to left

#

that's how you write function composition

verbal escarp
#

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

flat gazelle
#

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

verbal escarp
flat gazelle
#

yeah, I have seen the abominations that are large numpy expressions

verbal escarp
#

and they are neither pure scripters or mathematicians 🙂

main lynx
#

is there something wrong with writing it out over multiple lines?

flat gazelle
#

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

paper echo
# halcyon trail There's a few things that obviously could be improved, but overall I find loggin...

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

paper echo
main lynx
#

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

verbal escarp
#

@paper echo

paper echo
#

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
verbal escarp
paper echo
#

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.

verbal escarp
#

🙂

paper echo
#

but i actually think they are better in "app" development than in data science and scripting

verbal escarp
#

how so?

paper echo
#

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:

  1. syntactic macros (like R)
  2. currying (like Elixir)
  3. 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

verbal escarp
paper echo
#

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)

verbal escarp
paper echo
#

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))
halcyon trail
#

@paper echo i personally think a tutorial is a lot more appropriate than a whole library

verbal escarp
halcyon trail
#

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

main lynx
#

very recently used rshift when converting to and from 8/24-bit color

paper echo
#

i'm not advocating for loguru

#
logging.basicConfig(level=logging.INFO, logger='app.__main__')
logging.basicConfig(level=logging.WARNING, logger='app._internal')
halcyon trail
#

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

paper echo
#

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

halcyon trail
#

oh, really?

paper echo
#

someone here said that it did

halcyon trail
#

Okay, so my "I'm not convinced the author understands the point of hierarchical logging" was 100% on point lol

paper echo
#

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.

halcyon trail
#

Yeah....

paper echo
#

yes, you are 100% right and this is 100% misguided

#

this is literally for children and one-off scripts

halcyon trail
#

yeah, pretty much

paper echo
#

i.e. just use basicConfig

#

nobody should use this imo

#

i'm sure there are lots of useful utils and helper functions though

halcyon trail
#

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

paper echo
#

but you are right that the core idea is fundamentally misguided

halcyon trail
#

like, a lot of very superficial improvements

verbal escarp
#

if i need quick&dirty logging, i take q

paper echo
#

q?

verbal escarp
paper echo
#

oh my

#

this is actually really clever

verbal escarp
#

hehe

halcyon trail
#

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

paper echo
#

the syntax has the same energy as the perl q operator

halcyon trail
#

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

flat gazelle
#

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

halcyon trail
#

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

paper echo
#

right, it has to do with what the language lends itself to

#

clojure for example has lots of little functions

halcyon trail
#

python really pushes you fairly hard towards for loops for doing more complex things

paper echo
#

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

flat gazelle
#

yeah, I think it's the wrong direction to push, but python has enough other merits that I willing to overlook it

paper echo
#

probably not unlike code switching in multilingual contexts

flat gazelle
#

still want except ... try though

halcyon trail
#

well, it's also jarring mentally because nobody else does it, and achieving "pipes" in python is full of horrible hacks

paper echo
#

yeah

halcyon trail
#

and writing lambda over and over is boilerplate too 🙂

paper echo
#

but even a properly-designed thread-first operator would still be unnecessary abstraction in a typical python program

verbal escarp
halcyon trail
#

yes...

flat gazelle
#

yeah, writing an ast transformer and using exec while replicating calling context is a hack.

verbal escarp
#

pff. :p

flat gazelle
#

in the same way using encodings to get arbitrary syntax is a hack

paper echo
#

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?

halcyon trail
#

in Kotlin, this kind of "piping" code is, approximately ten times nicer than python. No exaggeration.

#

And still sometimes I use for loops

flat gazelle
#

@paper echowe did like 3 times over the course of this discussion

verbal escarp
paper echo
#

it is

flat gazelle
#

but I used it once in real code and regret it

#

just write a variable name multiple times

#

it much more readable

halcyon trail
#

or a for loop

#

tbh

flat gazelle
#

yeah, a for loop also helps

halcyon trail
#

One or the other, which one is more appropriate depends on the situation

verbal escarp
# paper echo it is

no, because many python functions don't operate on the first argument and assuming so breaks things quite quickly and dirtily

paper echo
#

you'd be surprised actually! but yeah that is one of the fundamental issues

#

partial and methodcaller help a lot

halcyon trail
#

I kind of dislike partial to be honest

flat gazelle
#

I should compile a list of things python would need to actually be good at large expressions

halcyon trail
#

and I also dislike currying

#

if your lambdas are good enough none of those features offers you much mileage, IMHO

verbal escarp
#

yeah, partial is nice.. @halcyon trail o.O?

paper echo
#

there's also thread_last @verbal escarp

halcyon trail
#

partial is nice in python, kind of, because python lambdas are horrible

quaint pike
#

What's horrible about them?

verbal escarp
#

oh god ^^

paper echo
#

the whitespace/indentation-based syntax fundamentally gets in the way of making them look and behave like def functions

halcyon trail
#

verbose, have to be one expression

#

so, everything?

verbal escarp
#

pretty much, yeah

paper echo
#

and yeah calling it lambda was a poor choice, should have been def or fn or something

halcyon trail
#

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

quaint pike
#

Can you assign a lambda to a variable? I forget. Maybe I tried and you can't.

halcyon trail
#

you can

flat gazelle
#

also, intentionally lacking some features

paper echo
#

you can

halcyon trail
#

although there is literally no reason to ever do that

paper echo
#

!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)

fallen slateBOT
#

@paper echo :white_check_mark: Your eval job has completed with return code 0.

3
verbal escarp
halcyon trail
#

lol

#

anyhow, you don't need

functools.partial(operator.eq, '.')

when you can just write

{ it == '.' }
verbal escarp
quaint pike
#

I have a lot more experience with Mathematica lambdas, but those are literally pattern matching rules, so they're very flexible.

paper echo
#

is it a monstrosity? i think it's actually quite elegant

halcyon trail
#

oh god, Mathematica

paper echo
#

with type annotations it'd be pretty straightforward to understand

verbal escarp
paper echo
#

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

flat gazelle
#

yeah, pointfree composition is often not desirable

#

it's really more for looking smart than being good code in most cases I have seen

halcyon trail
#

"pointfree" ?

paper echo
#
indent_text = (
    '\n'.join @
    '    {}'.format @
    methodcaller('splitlines')
)

😎

flat gazelle
#

no variables, just function calls to create a program

halcyon trail
#

taking it to an extreme is ridiculous, sure

#

but small snippets?

quaint pike
#

Functional programming is very elegant until it isn't.

verbal escarp
#

"pointfree" actually means "pointless" duck and cover

halcyon trail
#

I don't see an issue with chaining a few operations in a row. It really depends what they are, the context, etc.

flat gazelle
#

even in small cases, this is just horrendous

(comp first (partial remove (partial > 0)) (juxt - identity) identity)
halcyon trail
#

I don't know clojure so hard for me to comment

paper echo
#

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

halcyon trail
#

Yeah. Can you explain what this does?

#

then I can rewrite it in a readable language 😉

paper echo
#

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

flat gazelle
#

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)))
paper echo
#

juxt is kind of wack for this

flat gazelle
#

yeah, this is clearly a case of someone trying to be clever

paper echo
#

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

flat gazelle
#

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)

halcyon trail
#

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

verbal escarp
#
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

paper echo
#

(that is function composition so the order is reversed for extra confusion)

verbal escarp
#

exactly

halcyon trail
#

so methodcaller goes first?

verbal escarp
#

the input is on the bottom

flat gazelle
#

at this point I am more used to function composition order than shell order tbh

paper echo
#

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!!

halcyon trail
#

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

paper echo
#

yeah methodcaller i guess was supposed to paper over lambda but it's super verbose

#

the entire operator module is like that

#

!d operator

fallen slateBOT
#

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:

halcyon trail
#

the thing is that these things all look pretty bad in python

paper echo
#

btw we are kind of implicitly ragging on the toolz module but it's really a great library

#

!pypi toolz

fallen slateBOT
verbal escarp
halcyon trail
#
   
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

verbal escarp
#

it doesn't have to be a standalone hack

paper echo
#

i agree quicknir

halcyon trail
#

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

paper echo
#

well in haskell the function composition is well-typed

halcyon trail
#

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

verbal escarp
#

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

candid pelican
#

good night guys...

#

how python is made???

spark magnet
#

(short answer: it's a big C program)

grave jolt
#

(long answer: CPython is a big C program 🙂 )

candid pelican
#

what cpython is? i heard smth abt it.

elder blade
#

Python interpreter written in C, hence, CPython.

candid pelican
#

what the main difference between python and cpython ??

grave jolt
candid pelican
#

cpython is the interpreter??

grave jolt
#

it's an interpreter

candid pelican
#

ic

grave jolt
#

There are other ones, like PyPy, which has some different properties

candid pelican
#

mypy

grave jolt
#

mypy is just a tool for type checking, it's not an interpreter

candid pelican
#

ic

#

jython maybe?

charred pilot
#

jython is one

candid pelican
#

but still its python??

#

or its kinda a dialect

raven ridge
#

It's an implementation of (an old version of) the Python language. Others include IronPython, MicroPython, CircuitPython, pycopy, and arguably cython.

grave jolt
#

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)

charred pilot
candid pelican
#

ic

peak spoke
#

ideally anything you'd call "python" would behave the same for the user beyond implementation details

raven ridge
#

how do you figure micropython is a dialect?

candid pelican
#

i got it

charred pilot
#

what does dialect even mean in this context?

peak spoke
#

doesn't micropython take out some core features?

raven ridge
#

Cython is arguably a dialect, in that it is a superset of Python. It interprets Python, plus some other stuff.

raven ridge
candid pelican
#

like theres some benefits to use cpython instead python interpreter?

raven ridge
#

CPython is the Python interpreter. It's the normal one.

candid pelican
#

so when i type on the terminal

python file.py

im using cpython?

peak spoke
candid pelican
#

hm

#

noob

#

lol

#

how long do you code?

raven ridge
#

!e ```py
import sys
print(sys.implementation)

fallen slateBOT
#

@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')
raven ridge
#

^ The name of the implementation of the Python interpreter is "cpython".

candid pelican
#

woah

#

the bot is running in a linux hehe

#

hacker

raven ridge
candid pelican
#

i completed one year last month lol

#

what is the best way to practice programing?

#

games i think...

#

it is so logicals

raven ridge
#

!projects has some suggestions.

fallen slateBOT
#

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.

grave jolt
#

I guess the minor syntax differences are enough

raven ridge
#

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

candid pelican
#

i made a multiplayer game some days ago

raven ridge
#

!e print(1or 2)

fallen slateBOT
#

@raven ridge :white_check_mark: Your eval job has completed with return code 0.

1
grave jolt
prime estuary
#

It's quite hard to read syntatically, so it'll raise a deprecation warning now.

candid pelican
#

!e print(1 and 2)

fallen slateBOT
#

@candid pelican :white_check_mark: Your eval job has completed with return code 0.

2
candid pelican
#

why it happens

raven ridge
candid pelican
#

the output shouldn't be 12

raven ridge
# candid pelican why it happens

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.

candid pelican
#

ic

raven ridge
#

neither of them even evaluate the right argument unless they need to.

#

!e 1 or print("hello")

fallen slateBOT
#

@raven ridge :warning: Your eval job has completed with return code 0.

[No output]
raven ridge
#

!e 0 and print("hello")

fallen slateBOT
#

@raven ridge :warning: Your eval job has completed with return code 0.

[No output]
raven ridge
#

!e 1 and print("hello")

fallen slateBOT
#

@raven ridge :white_check_mark: Your eval job has completed with return code 0.

hello
candid pelican
#

like a ternary operator

raven ridge
#

!e print("nope") if False else print("yep")

fallen slateBOT
#

@raven ridge :white_check_mark: Your eval job has completed with return code 0.

yep
candid pelican
#

ye

#

be a generalist or a specialist

#

?

raven ridge
#

generalist generally gives you more opportunities.

#

I wouldn't specialize until later in your career.

candid pelican
#

i buy it

grave jolt
#

but don't become a thin layer generalist like me 👀

#

that's terrible

candid pelican
#

i'm 15 it's kinda confusing lol

#

in 3 years i gonna be an adult lol

#

how do you study programming??

charred pilot
#

the same way you study anything. read about it, practice it, watch videos, whatever you do to study

candid pelican
#

mhm

raven ridge
#

experiment, largely.

#

you learn to draw by making a lot of shitty drawings. You learn to program by making a lot of shitty programs.

paper echo
#

!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)

fallen slateBOT
#

@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)
paper echo
#

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)

fallen slateBOT
#

@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)
grave jolt
#

to continue the analogy, I'm a big shitty picture, like a nanopoop smeared across a stadium

main lynx
#

conditional iter comprehensions are how to python fast, it's true

#

it's a tighter loop to not yield up to the any

surreal sun
#

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?

main lynx
#

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

surreal sun
#

Ahh

#

since it's explicitly a boolean, it doesn't need to yield back to all

#

any*

#

until it's done

main lynx
#

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

paper echo
#

tldr no peephole optimizer = python slow

#

(mostly joking)

surreal sun
#

so does any have to evaluate truthy values and therefore the iter has to yield back to any? if it isn't a boolean

paper echo
#

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

swift imp
#

im dying for these python speed ups for 3.11

paper echo
#

@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)

swift imp
#

idk if anyone has been following and has an idea of the speed ups

paper echo
#

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

swift imp
#

Yes. Microsoft is hosting it

surreal sun
swift imp
#

Mark Shannon was hired to work with GvR at microsoft. They had hirings for it earlier in the yera

main lynx
#

looks like they want to target the bytecode since it doesn't need to be compatible across versions

paper echo
#

interesting

#

why not just put money into pypy?