#internals-and-peps

1 messages Β· Page 100 of 1

undone hare
#

I agree with the rejection

visual shadow
#

Where do I see the rejection?

undone hare
#

Uuh it should have been somewhere in the PEP

visual shadow
#

Yeah i guess this isn't updated yet? Other rejected peps had it on top

#

I thought It wasn't added but I was curious about the explanation in the rejection

flat gazelle
visual shadow
#

Awesome, thanks!

oblique crystal
#

where are those 10 parahraphs on hooks? can't seem to find

spark magnet
#

my old friend

#

settrace will be difficult to support in a language agnostic way.

#

(assuming anything truly can be)

oblique crystal
#

funny that it still have GvR as BDFL

visual shadow
#

"for life"!!!!

raven ridge
#

No takesy backsies.

weary garden
#

I will not be implementing settrace; there will be no Python debugger; there will only be a language agnostic neos debugger.

spark magnet
#

to answer your question, PyPy and Jython support settrace

weary garden
#

settrace is an implementation detail.

gleaming rover
#

I feel like

#

adding keyword arguments to indexing is a p weird thing

spark magnet
weary garden
spark magnet
#

i see πŸ™‚

#

probably in your environment people wouldn't need it, so it will be fine.

weary garden
#

indeed. the neos debugger will be wicked.

#

first language agnostic debugger perhaps and first debugger rendered in OpenGL perhaps.

#

the neos AST can be visualised as nodes and there will be a display option for the deubgger to render in node form as well as text form

#

why nodes? hipsters like nodes. πŸ˜„

spark magnet
#

i really think it's better to avoid "there will be", and stick to "there is"

weary garden
#

that wouldn't be wise, avoid "there will be" means you have no plan, no vision of what you want to achieve; the now is an artefact of the future.

spark magnet
#

oh, i definitely think you should plan, and have a vision, etc. But advertising it here as "there will be", when you have a long way to go, just starts tricky debates.

#

it doesn't serve your purpose.

weary garden
#

"there will be" is no different to stating a function requirement which is standard software engineering practice. define requirements first.

#

functional*

spark magnet
#

i'm building a pogo stick that will go to the moon. it's one of its functional requirements.

weary garden
#

sounds fun. good luck with that. πŸ™‚

spark magnet
#

exactly πŸ™‚

weary garden
#

however you have regressed to claiming that my solution is impossible again which is a shame.

spark magnet
#

no, i'm saying we should skip the entire question, by talking about what is, or what you are working on right now, and how we can help.

weary garden
#

okey dokey. πŸ™‚

#

the goal for the next 4 weeks is to get "hello world" parsed, byte code emitted, libffi integrated with stdout being redirected to an OpenGL window. I will probably have to take a few days off work to achive all that though.

spark magnet
#

that includes the bytecode execution component also, right? Did I see somewhere that you have your bytecode defined?

weary garden
#

yes, it is a lot of work and unfortunately (or fortunately) I have a day job.

#

the only hurdle for my project is time.

spark magnet
#

do you have the bytecodes defined?

weary garden
#

I think I defined a subset when I last worked on neos several years ago

#

I need to create a linker too of course

#

lot of work but luckily a lot of it is fun rather than tedious

#

if that wasn't the case I wouldn't be doing it

spark magnet
#

these bytecodes look very low-level compared to (for example) the JVM or the Python bytescodes.

weary garden
#

that is deliberate

#

makes translation to machine code (JIT) less work

#

but that was my first stab at it; I may fettle it when I revisit it soon

spark magnet
#

but translation of python will be harder, and the jitting won't have access to the higher-level concepts.

weary garden
#

python doesn't go anywhere near the bytecode

#

the fact the source code was python is lost during compilation

spark magnet
#

right, i understand

weary garden
#

yeah I will definitely have to take some time off work to do it in 4 weeks :/

#

less talking about it in here I guess πŸ™‚

#

ADD and B(ranch) defined!

#

πŸ™‚

#

I think Brainfuck only has add and branch so it might be turing complete πŸ™‚

spark magnet
#

as always, we are interested to hear about progress, and can help with Python details

weary garden
#

do you get paid by the hour for writing that? πŸ™‚

spark magnet
#

i'll send you an invoice πŸ™‚

raven ridge
#

@weary garden so the Python code is lost when it's compiled, but you'll have a debugger. Does that imply the debugger will only be able to debug at the level of the bytecode, not the Python code?

#

if so, that's going to make Python code very verbose, and very difficult to debug. For a simple example of why, x + y in Python is defined to behave as essentially this series of calls:

def add(x, y):
    res = type(x).__add__(x, y)
    if res is not NotImplemented:
        return res
    res = type(y).__radd__(y, x)
    if res is NotImplemented:
        raise TypeError("Unsupported operands for +")
    return res

If the debugger forces you to step through that whole series of byte code every time it hits a +, it's going to get old very fast.

#

to say nothing of the fact that resolving type(x).__add__ or type(y).__radd__ may require hash table lookups, or even themselves execute arbitrary Python code if the type has a dynamic __getattr__ defined.

weary garden
#

@raven ridge no, when semantic concepts are folded the source code elements they refer to are registered and will be used by the debugger to display code, variables etc

#

the exact from of the debug information is TBD

raven ridge
#

But... Does that mean that "Python add" will need to be its own semantic concept?

weary garden
#

add looks like a function to me

raven ridge
#

well, yes and no - it's a function in the sense that any operator call is a function, I suppose

#

there is a definition in the interpreter for what + means, and it evaluates to a series of dynamic type lookups, attribute lookups, and method calls

weary garden
#

well like any debugger you can step into or step over code

raven ridge
#

sure. I guess I'm more asking about the level of abstraction you'd choose to expose to the user here. There's Python-specific concepts at play here for how + is defined to behave, and I suppose I'm wondering whether you plan to unroll/inline them by default, or expose them as though they're a function call, or what.

#

though as long as the user won't be seeing all of the byte code for a hash table lookup by default, it probably doesn't matter much.

weary garden
#

the debugger will be aware of language agnostic semantic concepts and I could add support to tag them in the schema so they are not decomposed by the debugger; not really thought about debugging in any detail yet.

brave badger
rich cradle
#

Nice.

#

That's very nice.

#

Who proposed it?

raven ridge
#

I'm surprised opinions are this strong about this one

deft pagoda
#

i guess the uses for it outside of pandas weren't very motivating

strange heath
#

don't people say that getters and setters are unpythonic

#

or something

spark magnet
#

yes. you don't need them

magic python
#

What was the use in pandas πŸ€”

native flame
#

The getitem and setitem dunders are for indexing

magic python
#

ah df[x=1] instead of df[df['x'].eq(1)], yeah that could make sense

swift imp
#

Personally I didn't care for it

#

I felt the idea of acting as a logical mask to be weird

#

And it wasn't doing enough to serve as a .loc replacement

sacred yew
magic hedge
#

Re: PEP 637 (and other divisive PEPs), I think that the deliberation process would be helped (to what degree, I'm uncertain) if users didn't get the impression that a proposal is inevitable prior to a SC approval. It's hard to imagine anything other than oppositional mindsets coming from that situation.

There are implicit signals in mailing list discussions (most subtle, some less so), which seem to accumulate by a weighting on each person's reputation. But where someone of good standing in the community takes a declarative approach to a proposal - does it not directly foster a sense of inevitability?

A well-meaning example is Raymond Hettinger's tweets on recent PEPs, of the form "In Python's near future, ..."
e.g. for 637:
https://mobile.twitter.com/raymondh/status/1364794069222707201

In #Python's near future, indexing will support keyword arguments.

These will be valid:

val = x[1, 2, a=3, b=4] # getitem
val = x[*(1, 2), **{a=3, b=4}] # getitem
x[1, 2, a=3, b=4] = val # setitem
del x[1, 2, a=3, b=4] # delitem

https://t.co/3iIezp6MIJ

Likes

257

raven ridge
#

I'm quite surprised 637 was rejected after 634 was accepted, frankly. The syntactic cost of pattern matching is clearly much higher, the ability to ignore it much lower, and the benefits are at least equally nebulous

magic hedge
#

Agreed. Particularly given the emphasis on syntactic cost in SC's response.

The concern around performance for a very common process is more persuasive, personally. Edge cases may be as well, but I have not looked at those in depth.

uncut sage
#

I posted the other day about attempting to get type info for code objects processed with dis. Ended up rewriting what I was doing to use ast, and got tons of other benefits on top of it.

topaz helm
#

Im just wondering is this the right channel to speak about mathematics?

charred wagon
#

Generally, no

oblique crystal
#

that is a "Pythonic" way of doing getter/setter, isn't it ?

limpid marten
#

Yes, if you needed specific behavior that would usually warrant a getter/setter, you'd use a property instead.

spark magnet
#

@oblique crystal the classic "trivial getters" you see in Java are unnecessary in Python.

visual shadow
#

The main thing to note is that property allows your end users to keep using the class object as if it was still giving an attribute, while allowing you to freely add the benefits of custom logic later if need be. Thus it essentially solves the problem of breaking an interface that would have happened if you hadn't defined getters and setters upfront in a different language.

oblique crystal
#

yeah I get that. For me I view the properties in Python as improved way of doing getter/setter πŸ™‚

#

so here when you said "you don't need them" you meant not a python way with properties but Java-ish geo.getCoord() and geo.setCoord()

feral cedar
#
class Student:
  def getGrade():
    return self.grade
  def setGrade(grade):
    self.grade = grade
  def getName():
    return self.name
  ...
``` yeah, don't do this
grave jolt
#

yeah, if you need a public property, use a public property

ivory lagoon
#

I have questions regarding the certificate details (those you can check near the URL) but I have no idea to ask such questions because it isn't exactly python... or coding for that matter. Where could I go?

ivory lagoon
#

the one you get from the lock symbol ya know?

fallen slateBOT
rich cradle
#

@ivory lagoon This might be a good place to go.

ivory lagoon
#

How are they related to my unrelated topic tho?

grave jolt
#

@ivory lagoon questions unrelated to Python go to off-topic channels

rich cradle
#

The names are random.

#

They change every day, and do not pertain to the conversations.

boreal umbra
radiant fulcrum
#

still find it weird that they reject 637 but pattern match got in despite the divide in the community about if it should be different or not be added at all

shell yoke
undone hare
#

Well, in this case have you seen our asking good questions guide?

shell yoke
#

yup i did

boreal umbra
undone hare
#

What does keyword indexing have to do with this? thinkies

boreal umbra
undone hare
#

Interesting

#

Why not making it a function call though?

boreal umbra
#

because generic types use square brackets

#

I want this syntax and I want it now

peak spoke
grave jolt
#

besides, type checkers probably won't work well with anything like that

#

you'll need type-level lists

grave jolt
strange heath
#

linked lists

grave jolt
boreal umbra
#

It was a pipe dream for sure. A pandas type compatibility checker would probably have to be its own tool.

grave jolt
#

well, maybe a plugin to mypy

#

that could work

boreal umbra
grave jolt
#

is its pugin system that limited?

#

I mean, there's a plugin for attrs

rigid swift
#

With the keyboard lib, it's possible to hook on the windows key system and, for example, create new shortcuts. But is it possible to disable some ? For example, detect that a specific shortcut is use and prevent its action ?

limpid forum
rigid swift
#

thank you for the clarification. I didn't really know where to ask, as this is not really a "help" thing, and the python-general is overloaded

#

I already created my own shortcuts with this lib, and using Qt I know it's possible (I would have to double check tho) to disgard events

#

I guess I'll have to see for myself

limpid forum
#

You could try looking for contact info on the lib's website and ask people responsible for it

true ridge
boreal umbra
grave jolt
#

(because of the attrs example)

severe lichen
#

Honestly, I feel that choosing indexing for generics is bound to clash with indexing in general

#

Our keyboards have 4 types of brackets and we use the most popular ones

rich cradle
#

I only thought there was (), [], and {}.

radiant fulcrum
#

ig if you include <>?

rich cradle
#

Oh, yeah ig.

severe lichen
#

Yeah, triangular brackets. C# uses them for generics, for example, if my memory serves right.

charred wagon
#

Only the square brackets and parenthesis would work without introducing even more new syntax.

#

I wonder if parenthesis are feasible. Why weren't they chosen? Passing arguments to functions has the most powerful syntax.

#

Maybe it'd be too confusing to have them look like functions/classes

severe lichen
#

I don't think that new syntax is bad thing for new concept (generics). But again, I don't mind verbose langs.
Well, you already pass arguments to types, it's constuctors, and types are already valid arguments. At least you usually index instances, not types themselves.

charred wagon
#

Yeah, they did have to introduce new syntax anyway to support annotations. However, I don't know about all the implications of adding new syntax. There are probably considerations beyond my understanding.

sour dragon
#

Have a design dilemna

uncut sage
#

For any who are curious, this is the project I've been working on the last day or two. https://github.com/syegulalp/myjit

sour dragon
#

https://github.com/AirikWarren/Pyguin

Working on my static site generator to make it less specific to the websites that I hacked together at first and having a hard time deciding what a good and versatile / robust way to deal with allowing ease of extendability when it comes to extending the rendering function

#
    env = Environment(loader=FileSystemLoader(f'{template_dir}/html_templates'))
    filenames = [x for x in env.list_templates() if "base.html" not in x and not x.startswith("_")]
    nav_links = create_title_extension_dict(env)
    stylesheet_path = f'{template_dir}/css_templates'
    for i in zip(filenames, nav_links):
        f = open(f'./output/{i[0]}', "+w")
        f.write(env.get_template(i[0]).render(title=i[1], nav_links=nav_links))

This is what needs to go

#

Not every simple static site template is going to just need a single dictonary of page titles and relative links

radiant fulcrum
#

Or actually more numba witb explicit type declarations from the type hints vs numba's manic signature spec

uncut sage
#

@radiant fulcrum partly! But also I wanted to start with something that wasn't dependent on numpy, and maybe add that in later - start with something that could generate very simple binaries

radiant fulcrum
#

Good idea

uncut sage
#

I also liked the idea of having something that could generate "sidecar binaries" the way Cython does -- you run it, get an AOT-compiled version of your function, and you can distribute that instead of the whole of LLVM

#

The exact details of how that works are TBD but I'd like to make it as transparent as possible for the developer - they don't have to undecorate functions or anything like that

#

And if it eventually turns into its own language...

uncut sage
#

My first goal would be to get enough functionality to be able to have it run Conway's Life -- basically, loops, if, arrays, a couple of other things

#

andC FFI of some kind.

#

I'm still trying to figure out what would be a good way to express C calls in this style

#

perhaps

from myjit.ffi import malloc
...
x:Ptr[Byte] = malloc(32)
grave jolt
#

@uncut sage will you add sum types? lemon_hyperpleased

#

aka ADTs aka variant records aka Rust enums

uncut sage
#

@grave jolt slow your roll, son :D I'm still just getting my footing here. But nothing says in time I couldn't, I suppose

#

the idea would be to use Python's syntax and type decos, but not use Python's extreme dynamism

grave jolt
#

ah

uncut sage
#

this is also an arena for me to gradually learn all kinds of other things!

signal tide
#

Is there a way so affect how a class’s methods are run? I was thinking of somehow dynamically applying a decorator to all functions in the class but I’m wondering if that’s even possible/if there’s another way

cedar ice
#

@signal tide Cant you add a normal decorator on all classes and then have logic so that it runs it either normally or modifies the decorator as you want it to?

signal tide
#

Yes but I don’t want to add a decorator to all methods manually

peak spoke
#

decorate the class then

signal tide
#

How would that work?

peak spoke
#

iterate over the methods in the class, replacing them with versions wrapped in your behaviour

limpid marten
# uncut sage For any who are curious, this is the project I've been working on the last day o...

I recently read an article on RedHat about why Ruby avoided using LLVM for their JIT due to it being slow and incredibly large. Not that this should deter you I just thought it was interesting, but in case you wanted to also read it https://developers.redhat.com/blog/2020/01/20/mir-a-lightweight-jit-compiler-project/
I've played around with keystone engine and PeachPy in a similar way as well and it's pretty awesome and very cool what you're doing!

uncut sage
#

If MIR comes along with a Python wrapper, I might play with that

limpid marten
uncut sage
#

@limpid marten Been playing with it for a while now, so I can offer pointers. I'm actually wondering if there's another channel better suited to this particular discussion but I can't think of one - #c-extensions doesn't really fit, does it?

limpid marten
#

Is there? JITs are an advanced-discussion at least I hope so, and it's related to Python. PES2_Shrug

uncut sage
#

Seems like there could be a performance/speed related channel eventually but this will do for now

limpid marten
#

I'm not too familiar with LLVM IR, but is there a concern with register allocation?

uncut sage
#

in what sense?

limpid marten
#

Like if you were instead creating your JIT using x86 assembly, you'd have to be mindful of how many locals you have, and arguments passed to the function and if you need to either have a variable on the stack or in a register for your JIT'd function.

#

But it looks like LLVM IR is a bit higher level than that.

uncut sage
#

Oh, LLVM abstracts all that

#

here's some code I created

#
def test() -> Byte:
    x: Byte = 8
    y: Byte = 16
    return x + y + 32
limpid marten
#

Ah, do you read annotations?

uncut sage
#

here's what LLVM turns that into with my JIT

define i8 @"main"() 
{
entry:
  %"x" = alloca i8
  store i8 8, i8* %"x"
  %"y" = alloca i8
  store i8 16, i8* %"y"
  %".4" = load i8, i8* %"x"
  %".5" = load i8, i8* %"y"
  %".6" = add i8 %".4", %".5"
  %".7" = add i8 %".6", 32
  ret i8 %".7"
}
#

yes, the whole idea is to have static typing with Python's annotation syntax, or somethign close to it

limpid marten
#

ahh, nice, so you don't have to call Py_Add

#

or, whatever the function is.

#

That's really awesome though!

uncut sage
#

right, I'm not doing anything with the Python runtime yet

#

it's all resolving to native machine types

limpid marten
#

which is probably what you want anyway

uncut sage
#

exactly. This is meant for specific functions that need de-bottlenecking, mainly numeric functions but I do want to make it possible to do things like string manipulation eventually

#

so in time it might grow to become its own AOT/JIT language

limpid marten
#

Man is searching the c pythonapi a pain.

uncut sage
#

My next step is to add support for arrays, as one of the "milestone 1" projects I have in mind is to implement my Conway's Life demo using this as an accelerator

limpid marten
#

Ahh, that'd be awesome.

#

Have you tested how long it takes for Python to construct ctypes types?

#

I know there is some overhead with that.

uncut sage
#

well, with the decorator, there should ideally only be a one-time cost to wrap the function.

#

basically, we compile the function, then get in and out types ahead of time, then generate the function wrapper with the ctypes objects already constructed.

#

Ideally, you'd use this for a function that you'd call and would have a lot of internal overhead. In time I'd like to generate something where you can have a native Python extension module

limpid marten
uncut sage
limpid marten
#

Yeah of course! πŸ™‚

#

One thing that scares me is trying to debug a JIT compiler, but I guess since you can just look at the IR that was generated it would make it a bit easier.

uncut sage
#

yup, the IR is dumped with each run for now as a debug gesture

limpid marten
#

Please keep me updated on your progress, things like this are so interesting!

uncut sage
#

thank you! Glad someone is curious about it :D

#

PLZ FOLLOW & STAR ON GITHUBZ THX

rich cradle
#

What are you making?

uncut sage
rich cradle
#

Interesting. I'll take a look at it,

true ridge
raven ridge
#

ctypes? Seems like it would be both easier and faster to take the route that Cython cpdef functions take, and have a Python callable trampoline that uses, e.g., PyLong_AsLong to convert a Python int it recieves to a native int suitable for passing to the compiled function

#

Likewise you'd handle strings by calling PyUnicode_AsStringAndSize and passing a char* and a size_t down

uncut sage
uncut sage
#

I can start with using ctypes, and then migrate that out as I get more familiar with how the C API works

#

right now I just want to get the basics running, even if not optimally implemented

raven ridge
#

Fair enough, but that seems like it might wind up being a lot of throwaway work if you invest too much into ctypes.

#

This all definitely feels on topic for #c-extensions to me, FWIW. Adding JIT compiled functions is definitely "extending Python using compiled extensions"

uncut sage
#

well, I'm trying not to make too much work in that department upfront, most of my work is on the guts of the thing for now

#

also, yes, I'll probably make more discussion about this in #c-extensions later. Got to get to work now :D

limpid marten
raven ridge
#

I find ctypes much harder to use than the C API.

#

Boilerplate of creating a new extension module aside, at least.

uncut sage
#

right, I'm just using it to pipe things into and out of the module for now

#

::drags self bak to work::

agile stump
#

Hi

uncut sage
#

@median palm Scroll up for a discussion of a project that might be of innarest to you

median palm
#

Alright

#

Ooh cool, I'll check it out, thanks :D

#

Starred the repo :D

uncut sage
#

@median palm thanks! It's still in the early stages, but it builds on many of the things I was toying with in my earlier compiler project

limpid marten
#

Just don't let us down, no pressure. πŸ˜†

uncut sage
#

Heh :D

#

I already have the next wave of changes on the way.

median palm
#

Lol

#

I looked through it, it seems cool

uncut sage
#

Be a while before it's useful, but one thing at a time

median palm
#

Get it to work first, right?

uncut sage
#

Yes, get a proof of concept with a demo (Conway's Life is my favorite demo)

willow ingot
#

can someone tell e a way to convert pinescript to py

boreal umbra
obsidian sundial
#

hi, does anyone know about naka rushton's equation for a neural network project

strange heath
obsidian sundial
#

Ty

brave badger
#

I can't wait for ParamSpec to finally be a thing

hard jolt
#

I've been wondering how many libraries on PYPI that are explicitly about working with data (i.e. not doing Network or Disk I/O) actually mention time-space complexity of their public functions/methods?

#

Does everyone just code like YOLO, "that's premature optimization, dawg", we'll profile that shit later and optimize it, so just ship itβ„’

brave badger
#

I'd assume that if it really required benchmarks, some library could use regression tests as part of their CI

flat gazelle
#

if you are operating at a scale where complexity matters, your data structures likely aren't written in python

undone hare
#

Hello @unkempt rock, this channel is for Python related talks, can you ask in off-topic please?

civic slate
#

Should I switch to python 3

undone hare
civic slate
#

Is there any major difference?

undone hare
#

!ot You can send it in off-topic

fallen slateBOT
median palm
# civic slate Is there any major difference?

Well, python 3 has a slightly differential syntax, as well as other structural differences (i think), and python 2 isnt supported at all anymore, while python 3 has loads of support, as well as new features coming to it

undone hare
# civic slate Is there any major difference?

I don’t know a lot about Python 2, but I think the main features are that you don’t need all the iterable version of builtins, it is now the default, division of two ints will give you a float, and print is a function

civic slate
#

If I learned python 2 how long do u think it would take for python 3

undone hare
#

Not long I think

#

There aren’t much differences

civic slate
#

Ok Thanks!

raven ridge
#

That's the one that made porting from Python 2 to 3 difficult. Everything else is trivial in comparison

undone hare
#

Right

silk pawn
#

so if we have something like

x = (1, 2, 3, 4, 5)
if len(x) == 1:
    do_something()

will it iterate through x to count the number of items, then compare that number to 1, or will it only iterate until the number of elements counted is greater than one, in which case the condition can't be True?

#

or is there another way that it gets the number of items in x

oblique crystal
#

does it even need to iterate?

silk pawn
#

Β―_(ツ)_/Β―

raven ridge
#

It doesn't iterate at all.

oblique crystal
#

I don't think so, tbh

raven ridge
#

A tuple knows its size.

silk pawn
#

ok what about a list

grave jolt
#

Same.

raven ridge
#

Same, a list knows its size.

silk pawn
#

oh

grave jolt
#

Yep, a list is just a dynamic array in memory. It's a single, contigous piece of memory

silk pawn
#

so if i do

if len(list(mydict.keys())) == 0:

then it's constant time?

grave jolt
#

Nope, constructing a list is O(n)

#

You can just do len(mydict) == 0

silk pawn
#

sorry i meant assuming that we ignore the time taken to turn mydict.keys() into a list

grave jolt
#

or... mydict == {}

silk pawn
#

oh

#

nice

#

i'll do that then

#

thanks!

raven ridge
#

Or if not mydict:

silk pawn
#

oooooh

grave jolt
#

well, I'm not particularly fond of bool coercion, but if you like that, do that

#

!e
If you had a linked list, you'd have to traverse it, yes.

xs = (1, (2, (3, (4, (5, None)))))

def llen(xs):
    if xs is None:
        return None
    _head, rest = xs
    return 1 + len(rest)

print(llen(xs))
fallen slateBOT
#

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

3
raven ridge
#

A linked list that didn't store its size. But even for linked lists, that's a common optimization.

grave jolt
#

Yep, yep,

raven ridge
#

At least, for general purpose linked lists.

grave jolt
#

another example is a null-terminated string

silk pawn
#

doesn't C use null-terminated strings

grave jolt
#

it does

raven ridge
#

That's arguably the single biggest shortcoming of the C language.

grave jolt
#

hehe

#

maybe

magic python
#

I'm thinking about writing something that'll call a function based on whether the output of the function ( some file ) is older or newer in time (mtime) than some dependency.

Kinda like makefiles

I thought this would be a thing, but I can't seem to find it, so I'm not sure if it's an awful idea

#

Hm, seems there's doit, wish I found that earlier

limpid marten
#

Kinda like a build system? Sounds like fun.

gleaming rover
silk pawn
grave jolt
#

It can also cause silent bugs, e.g.:

if order.is_shipped:
``` this will always succeed, because it's a method, and should've been written as: ```py
if order.is_shipped():
silk pawn
unkempt rock
#

rofl

grave jolt
sacred tinsel
#

i personally like the fact that it's implicit
think of the zen πŸ˜”

grave jolt
#

which I don't understand

sacred tinsel
#

implicit coercion lets us do things like

return primary or get_fallback()
#

which I always think is neat, but is it better?

#

probably not, I dont know

grave jolt
#

it can be a trap with values that can be falsey

sacred tinsel
#

but maybe thats the intended behaviour

#

if primary can be both None and an empty set, maybe you want exactly what you're getting there

grave jolt
sacred tinsel
#

but they're not the same type

#

isnt that the point?

grave jolt
#

by type I mean a more extended understanding... like, the set of values

sacred tinsel
#

the type decides whether it needs to be replaced by the fallback

grave jolt
#

If you go with that interpretation, the semantics for Optional being empty is that it's None.

#

maybe it could be resolved by introducing some type like Truthy, which is a proxy that's always truthy. So Optional[Truthy[list[int]]]

sacred tinsel
#

what do I read to expand my knowledge on types and the surrounding theory? because you seem to be able to think about them in a way that fascinates me a little bit

grave jolt
#

πŸ‘€

#

uhh

#

idk

#

I don't know anything about type theory, really

sacred tinsel
#

😩 I know less

grave jolt
#

Well, in general, it's sometimes helpful to think of types as sets of possible values instead of whatever your language calls a type.

#

@sacred tinsel are you familiar with variance? (invariance/covariance/contravariance)

sacred tinsel
#

familiar yeah

#

in the sense that I always look it up when I need it anyway

grave jolt
#

So function are contravariant in the parameter types. It means that if X is a subtype of Y, then Y -> Z is a subtype of X -> Z, for example:

A = Union[int, str] -> bool
B = int -> bool
int <= Union[int, str]
A <= B
``` but the same can be said of any other contract. For example, suppose that `f` takes a function that expects a positive integer and returns a string. It follows then that `f` can also accept a function that expects a non-negative integer or any integer at all; but it wouldn't necessarily work with a function that expects even integers.
#

My point is that it can be useful to think of different contracts as of types. Python already does that -- there are things like Union and Callable and Hashable and Protocol which describe contracts, but aren't really types, i.e. classes.

raven ridge
sacred tinsel
#

but it wouldn't necessarily work with a function that expects even integers.
why not?

grave jolt
grave jolt
sacred tinsel
#

okay, so it follows that f can accept a function that expects a non-negative integer because non-negative integers are a superset of positive integers

#

so we're not violating anything

grave jolt
gleaming rover
fallen slateBOT
#

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

001 | []
002 | []
gleaming rover
#

also telling that numpy explicitly doesn’t let you do that

raven ridge
grave jolt
#

hiding errors is a feature?

raven ridge
#

you're defining it to be an error, not the language

grave jolt
#

well, yes

raven ridge
#

the programmer chose a construct that produces some particular behavior when None is given as an input. That may be desired behavior, in which case they leveraged a feature, or it may be undesirable behavior, in which case they have a bug.

#

I generally like bool coercion, but I have accidentally checked for the truthiness of a function, instead of the result of calling the function. I can see how making some things fail to implicitly convert to bool would be a win, instead of allowing it for all objects.

#

I think my criteria would be that as long as there's some instance of a given type that could convert to False, I'd allow other instances to convert to True. If every instance of that type will always convert to True, I'd prevent coercion to bool.

grave jolt
#

yeah, maybe not all objects being coercible is a good idea

gleaming rover
raven ridge
#

well, let's see. That's statically determinable for every builtin type. There exists some value for int and list and float and dict for which it's falsey, etc. There exists no such value for function or method, etc. For user-defined types, by default every instance of every user defined class is truthy, so user defined types would not be coercible to bool by default. If you add a __len__ or a __bool__ to them, they would be.

grave jolt
#

same should be true for __hash__, perhaps?..

raven ridge
#

something similar is done for __hash__

gleaming rover
#

unless you’re saying

grave jolt
#

!e

class A:
    pass

print(hash(A()))
fallen slateBOT
#

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

8774510714766
gleaming rover
#

assume that one exists if you can’t prove that it doesn’t

raven ridge
#

right - we can know the reverse. We can't know for certain whether a falsey value exists, but we can know for certain when no falsey value could exist.

grave jolt
#

if the object has a __bool__ method, there must be some instance which is falsey (that's how I understand godlygeek's proposal)

raven ridge
#

not necessarily, but most likely

#

they wouldn't have added it for no reason.

gleaming rover
raven ridge
#

sure. Most likely.

#

!e ```py
class A:
def eq(self, other):
return True

hash(A())

fallen slateBOT
#

@raven ridge :x: Your eval job has completed with return code 1.

001 | Traceback (most recent call last):
002 |   File "<string>", line 5, in <module>
003 | TypeError: unhashable type: 'A'
raven ridge
#

we do something like this for hash, already. User-defined types are hashable by default, unless they define a __eq__ in which case they're not, unless they define a __hash__

#

it wouldn't be ridiculous to say that instances of user-defined types are not coercible to bool by default, unless they define __bool__ (or __len__ perhaps).

gleaming rover
#

I don’t like boolean coercion in general, but it does kind of fit the Python model

#

more specifically I’m against empty containers being falsey

grave jolt
# gleaming rover why

Hm, I sort of disagree with that now.
It would be a contract that if an object has a __bool__ method, it makes sense for it to participate in a boolean comparison, not necessarily that there's an empty element.
An example would be a wrapper like Truthy (<#internals-and-peps message>), which is a proxy type (i.e. allows all other operations) but is always truthy. It is never 'empty', but it makes sense to use it in a boolean context.

raven ridge
#

right. In the same way as we say that the existence of __eq__ is enough to make us uncertain whether hashing is safe, and therefore prevent it, it's reasonable to say that the existence of __bool__ is enough to make us certain that coercion to bool is reasonable, and therefore allow it.

gleaming rover
#

fair enough

raven ridge
#

in the hash case, we don't look at what's in the __eq__ - it doesn't necessarily make hashing unsafe, it could be def __eq__(self, other): return self is other

silk pawn
gleaming rover
#

but specifically here, wtf is if []

grave jolt
silk pawn
gleaming rover
#

I am all for conciseness

#

but

grave jolt
silk pawn
#

indeed

raven ridge
#

I don't see this as a question of conciseness, mostly. I see it as a preference for duck typing. Knowing whether a container contains elements in a generic way makes it easier to write duck-type functions.

#

though I do suppose len can accomplish the same thing. But if len(foo) == 0 makes me feel sad, for a reason I can't put my finger on πŸ˜„

magic hedge
#

My go-tos for being explicit with collection predicates are if len(obj) and if not len(obj), since people often expect coercion and for integers it is unambiguous.

#

Although to be specific, this applies to any instance of collections.abc.Sized (i.e. implements __len__)

magic python
#

I find it annoying that dictionaries passed to functions are by reference rather than value, and often use deepcopy at the start of a function to avoid this

It's that common?

#

I don't want the function to have side effects I guess

wide shuttle
#

I think Ned Batchelder called it passing by assignment, as the parameters are assigned directly to the objects just like a regular assignment would.

#

I rarely use a deep-copy, although I mostly create new objects if I want to return something instead of mutating the old. Obviously, if you're creating a new outer dict with the elements of the old, you're still creating a situation of potential mutable aliasing (depending on whether or not the elements are mutable).

#

I think Fluent Python has a few lines on explicitly doing something like:

class SomeClass:
    def __init__(self, some_finite_iterable_data):
        self.data = list(some_finite_iterable_data)

but that's still only the outer container object.

undone hare
#

I don’t think I ever used deepcopy lemon_grimace

magic python
#

Hmm, so it's not a common annoyance?

peak spoke
#

Can't remember the last time I had to explicitly copy somewhere; in most cases the result is something completely new where it doesn't make sense to pass the old data through

grave jolt
#

@magic python Maybe you just want an immutable dict? πŸ™‚

undone hare
#

The only copy I used recently was to iterate on a list while editing it

magic python
#

i often have

def f(*, _dict):
    _dict = copy.deepcopy(_dict)
    <do stuff with _dict>
    return _dict
sacred tinsel
#

I find it a lot more common to build up the new object (eg with a dict comprehension) than to first copy the input and then mutate it

#

I dont find myself needing deepcopy

limpid marten
#

And as @grave jolt suggested, Python has no value types, so the closest you can get is with immutable.

magic python
boreal umbra
#

I've used deepcopy to guarantee that any data passed to certain functions can't be unexpectedly mutated

visual shadow
#

I've been in the same boat as rie, but I prefer the python approach to variable passing still. I don't want to take a performance hit by unnecessarily having python copy everything on my behalf when I don't need to in most cases

#

And once you're used to how variable passing works (I support "pass by assignment") it's fairly easy to understand the behaviour

magic python
#

@visual shadow yeah - i mean, i get how it works, i'm just not sure i like it?

#

tho it does make me feel i'm missing a point

limpid marten
#

Is it wrong to just say everything is pass by value in Python? Although I don't really think it's all that useful to name it as long as you understand the semantics.

#

Object references are passed by value right? So everything is pass by value?

magic python
limpid marten
#

It might not specify it's own __deepcopy__ implementation, given most of matplotlib is written in C++.

magic python
#

yeah it just failed : (

magic python
#

i know that R copies lists rather than references them though, for example

urban sandal
#

Mutable objects passed around have caused more bugs than I can count. We (work) started writing extension code for immutable objects, not just containers, and not for performance reasons. Larger the code base, the more likely an accidental mistake with it happens, especially with any amount of concurrency added in.

magic python
#

@urban sandal so this is anti by reference? would you prefer the deep copy behaviour as default too? or not

urban sandal
#

Still passed the same way, just objects defined in native extensions can be immutable. This is acceptable to avoid issues, and a new object with transforms on it can be created if needed.

peak spoke
#

apart from the fact that many objects can't be copied, recreating containers for non trivial sizes is usually a considerable amount of work for example

#

And to check that it can be copied you'd need to check the object on every call which would also be a performance hit

grave jolt
magic python
grave jolt
#

AFAIK Cpython uses HAMTs somewhere internally

#

well, if you have a dict of 10 values, it's probably overkill

magic python
#

i usually have nested dictionaries of survey data

urban sandal
#

The main problem we had with immutables (and why we cant just switch to it) is there isn't a way to efficiently create nested structures directly (such as during deserialization), also it didn't exist when we started doing this.

urban sandal
grave jolt
#

right

#

ye

visual shadow
#

Those terms fundamentally sit at the wrong level of abstraction in the context of Python imo

charred wagon
#

I disagree with it being technically correct, depending on how technical you want to be. Doesn't pass by value imply the value itself is copied? In Python, passing a class instance doesn't copy the whole object; it passes a reference to it.

#

It's technically correct if you say the reference is passed by value, just like you could say passing a pointer is passing by value because the pointer is copied.

raven ridge
#

Yeah, passing a Python object works the same as passing a Java object: it passes references by value

#

The terms "pass by value" and "pass by reference" are misleading when applied to modern languages. It's a false dichotomy.

#

C++ is an example of a language that has both pass by value and pass by reference. If you pass a vector<int> by value, it gets copied, creating a new heap allocated dynamic array. If you pass it by reference, then assigning to the parameter inside the function modifies the object. Python doesn't match either of those behaviors.

#

Python is not pass by value, because if you pass a list to a function, the function doesn't get it's own private copy.
It's also not pass by reference, because if you assign to that parameter in the function, the caller of the function does not see that change.

flat gazelle
#

I have seen the term pass by value reference

bronze steeple
#

Imagine that a user runs the below simple code.

with open(my_file, 'r+') as obj:
    res = obj.readlines()
    obj.write('bla bla')

Now, I want to be able to write to a different path.
Meaning allow the user to do all the reading they want from my_file but when they
do obj.write('bla bla') I want it to go to my_custom_out_file

How can I **intercept **the write() function to change the out file path?

unkempt rock
#

hi

#

is this where i get support?

#

oh nvm

rich cradle
raven ridge
#

this is a discussion channel about the Python language itself.

charred wagon
#

Otherwise you can look into trying to using mock.patch to wrap the return value of open.

bronze steeple
#

I think a custom open is the approach I will have to go with and update the implementation of write

deep bramble
peak spoke
#

Mind the note,

these methods are looked up on the type (metaclass) of a class. They cannot be defined as class methods in the actual class. This is consistent with the lookup of special methods that are called on instances, only in this case the instance is itself a class.

flat gazelle
#

same reason

def fun():()
fun.__add__ = lambda s, o: 54
fun + 5
``` doesn't work essentially
deep bramble
#

oh I see, I haven't noticed the note below, metaclass usage makes sense, thanks!

raven ridge
#

I'd never seen def f():() before, instead of pass or ...

#

That's some lovely code golf right there πŸ˜„

flat gazelle
#

not even good golf, 0 would work better

#

I just like how it looks

weary garden
#

I see Python almost has universal numbers:

#

and by that I mean the floating point literal has a precision exceeding double precision before converting to double precision.

#

kind of

radiant fulcrum
#

pithink pretty sure they're just the standard f64 types

raven ridge
#

1e-95 is in range for a double

spark magnet
#

but isn't double in 2.3E-308 to 1.7E+308 ?

radiant fulcrum
#

1.7976931348623157E+308f64

#

max

spark magnet
#

(though I got python to do 1e-323, which must be denormalized?)

radiant fulcrum
#

interesting

#

i think it's just cutting the overflow off implicitly

#

if you do 1e324 you'll get 0.0 so pithink

raven ridge
#

!e ```py
import math
print(math.ulp(0.0))
print(math.ulp(0.0).hex())

fallen slateBOT
#

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

001 | 5e-324
002 | 0x0.0000000000001p-1022
raven ridge
#

That's the smallest representable float.

spark magnet
#

cool

raven ridge
#

And yeah, that's denormal.

#

!e And the highest, for the sake of completeness, is

import sys
print(sys.float_info.max)
print(sys.float_info.max.hex())
fallen slateBOT
#

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

001 | 1.7976931348623157e+308
002 | 0x1.fffffffffffffp+1023
weary garden
#

I guess I meant it accepts literals with more decimal digits than the decimal precision of double but I guess that is fairly standard thinking about it, perhaps.

#

confirmation:

#

in Ada such literals are "Universal Numbers", i.e. infinite precision so would not give same result as above

radiant fulcrum
#

isnt that basically just Python's Decimal types

spark magnet
#

@weary garden Your decimal literal only has 1 significant digit, the one at the end, right?

weary garden
#

yeah

#

0.10000000000000000000000000000000000000000000000000000000000000001 obviosuly has more than 1 and more than double

spark magnet
#

maybe i'm confused about float terminology. it's not surprising that these float literals are supported, is it? they are within range

weary garden
#

0.10000000000000000000000000000000000000000000000000000000000000001 is not representable as a double

spark magnet
#

right

weary garden
#

but would be representable as a universal number in Ada

raven ridge
#

It's within range of a double, but not exactly representable, so you need to round it

weary garden
#

double only has 16 decimal digits of precision

spark magnet
#

how does ada represent it?

weary garden
#

it doesn't matter: it is a literal

#

infinite precision

#

so it is held as infinite precision by the compiler and then rounded to whatever precision the variable being assigned to is

spark magnet
#

i see, so the net effect is the same, it ends up rounded

weary garden
#

no it isn't the same

#

in my screenshot above the result from Ada would be different

spark magnet
#

what would the result be?

weary garden
#

because I have an expression (subtraction) involving literals not variables

spark magnet
#

i see

weary garden
#

in Ada the result would be 0.00000000000000000000000000000000000000000001 not 0.0

#

so I will have to DEGRADE my universal number semantic concept so it behaves as expected for Python which is a shame

pliant tusk
#

Where is that behavior of ada documented

weary garden
#

google ada universal number

#

ada universal numbers are numeric literals with infinite precision

pliant tusk
#

i cant seem to find it

#

can you send a link

spark magnet
#

@pliant tusk can't find what?

pliant tusk
#

documentation detailing ada having infinite precision float types

spark magnet
pliant tusk
#

neither do i, itll be a learning experience

raven ridge
weary garden
#

great

raven ridge
#

sounds like arbitrary precision float literals would be compliant, as long as they coerce down to double precision when needed.

weary garden
#

in which case I will use my universal number semantic concept for Python as-is \o/

raven ridge
#

Python doesn't specify the behavior for signalling nans, but most (all?) implementations treat them as quiet nan

weary garden
#

drinking my last bottle of Shiraz: need to buy a new case.

raven ridge
#

it does distinguish +0.0 and -0.0

#

I don't think it gives you the ability to distinguish different NaNs from one another.

#

IEEE 754 says the bit pattern of a NaN is allowed to convey information, but I don't believe Python floats give you any way to inspect it.

hexed aspen
#

I have a broad question (with very low stakes for my current purposes): At what point do you have to start worrying about memory consumption by a dict of dicts? Was kicking around the idea of implementing a simple cache in a class to improve init speeds very slightly. Top-level dicts would be keyed by ~10-character strings, and each internal dict holds about a dozen values--all short strings, ints, or None.

raven ridge
#

at the point where you can't afford the amount of memory it takes, I guess

#

if it takes 15 GB but your machine has 20 GB, who cares?

hexed aspen
#

Haha, fair.

visual shadow
#

Thus giving you exactly the behaviour you observe. At that stage though, we've introduced two concepts that python doesn't have, and imo should not need to be introduced when talking about just python

#

Re the cache question and the memory concern for dicts, both sound like premature optimization @hexed aspen

#

Don't worry about the speed until it actually shows itself to be an issue.

#

Don't worry about the memory consumption until it actually shows itself to be an issue.

#

(re memory consumption i may be the devils advocate and say that sometimes you have a decent grasp on a cache that would become too big in production, and there it's fine to think about it.)

#

I'm still conflicted on when that is the right thing to think about, but its definitely after you've built a working code and seen the timings, you may not need to cache in the first place.

spark magnet
visual shadow
#

yeah, agreed. I stick to just saying "pass by assignment" when explaining it, it's been the easiest one to convey by far in my experience

wide shuttle
#

I liked the "assignment" way of phrasing it in your talk, @spark magnet

#

I've basically just copied it and I'm using it myself a lot now

#

And also the "why do you worry about it?"

spark magnet
#

thanks πŸ™‚

wide shuttle
#

Do you have some spite-driven development for this year's PyCon?

spark magnet
wide shuttle
#

That will be an entertaining read.

#

Ah, yeah, another good talk. I've watched most of them, I think. I do love to watch Python talks.

spark magnet
#

i should watch more of them

peak spoke
#

I don't really like the terms, as for a person that doesn't already know how the system works it's indescriptive, and if they do know it it doesn't really need a special term imo

wide shuttle
#

I've compiled a list of good talks for the juniors I'm coaching at work. I think a lot of those talks contribute something that books and most courses don't.

peak spoke
#

pass by value, assignment, reference and the others discussed above

spark magnet
spark magnet
wide shuttle
#

I like the assignment explanation, as it neatly ties in with so many other things within Python that "secretly" assign names to values

#

Defining a function looks nothing like a simple assignment statement (like it does do in a language like R)

peak spoke
#

I'd probably explain it whole like any of you would - in a few sentences. I just don't like trying to fit it into a single term like pass by assignments as it really doesn't do much for someone that doesn't already know what's going on

spark magnet
#

true. a full explanation is warranted.

weary garden
#

@spark magnet only a false dichotomy in Python?

spark magnet
#

@weary garden no, it's a false dichotomy in many other languages.

weary garden
#

many?

spark magnet
#

more than two πŸ™‚

weary garden
#

it certainlly isn't in C++

spark magnet
#

ok

#

it is in Python, JavaScript, Java, Ruby, ...

#

perhaps one of the reasons people believe there are only two ways to pass arguments to functions is because they are stuck in C-thinking.

#

(or their professors are)

weary garden
#

passing scalar types by reference has a performance hit though: one reason why C and C++ are classed as "high performance" languages.

pastel epoch
#

is there a way to connect the users spotify with my python app?

spark magnet
#

i don't see why it would be slower to pass a scalar by reference. You copy the pointer instead of copying the scalar. it's the same amount of work.

weary garden
#

because of the potential overhead of dereferencing the pointer which is more likely to be a CPU cache miss

spark magnet
#

i see. certainly the "everything is an object" model imposes many small costs

unkempt rock
#

will python ever get static typing?

spark magnet
#

@unkempt rock have you tried mypy?

#

that's static typing

unkempt rock
#

huh?

#

ok

#

is it similar to cpython?

#

any major differences?

raven ridge
# weary garden <@!424559318617161740> only a false dichotomy in Python?

It's a false dichotomy because it suggests that every way of passing parameters is either pass by reference or pass by value. This is not true. Argument passing in Python works neither like pass by value in C++, nor like pass by reference in C++. Same with lots of other languages: Java, C#, JavaScipt, Ruby, etc.

#

In C++, if you've got

void f(int &x) {
    x = 5;
}
int main() {
    int a = 10;
    f(a);
    std::cout << a << std::endl;
}
``` it'll print out 5. You can't do that in Java or Python or C#. They don't pass by reference, you cannot rebind a variable in the calling scope to a new value
#

Even C only has pass by value. There's no way to rebind a variable in a calling scope with a function in C, you need to use a macro for that, or change the way to call the function (by passing a pointer to the variable instead)

spark magnet
unkempt rock
#

ok

spark magnet
weary garden
#

@raven ridge I am sure C++ isn't the only language to support pass by reference and pass by value.

wide shuttle
spark magnet
wide shuttle
#

It's a very creative way to debug the situation that I probably wouldn't have thought of

spark magnet
weary garden
#

@raven ridge Java has pass-by-value: the value being passed is the address of an object

spark magnet
#

I think the most important aspect of argument passing is: what do the caller and callee share, and how can each affect the other's view of the data?

weary garden
#

I will decide on terminology cromulency after doing a bit of research: the most pervasive view will win out.

spark magnet
#

In C++, if I pass an array by value, and the called function changes the array, the caller doesn't see the change. In Python, a called function changing a list, the caller does see the change.

#

the most pervasive, or the most accurate? and whose views are you tallying up?

weary garden
#

such cromulency will dictate the names I give to semantic concepts

spark magnet
#

@weary garden you've never seemed swayed by what the crowd does before, why "pervasiveness" now? πŸ™‚

weary garden
#

as neos allows any programmaing language to be specified in terms of semantic concepts the names I choose for such concepts has to be along the lines of the majority view in order to be the most inclusive for the understanding of a broad audience.

raven ridge
weary garden
#

Wikipedia will be my primary source as its peer review effectively meets my requirements

spark magnet
#

@weary garden Wikipedia calls Java's object technique "Call by Sharing"

raven ridge
#

I've never heard that one before πŸ˜…

spark magnet
#

(as well as Python, JavaScript, Ruby, etc)

#

it goes on to say, "However, the term "call by sharing" is not in common use; the terminology is inconsistent across different sources. For example, in the Java community, they say that Java is call by value."

raven ridge
#

Python's model is the same as Java's, except without the exception for primitives that Java has, because Python doesn't have primitives.

spark magnet
#

@weary garden in my experience, people will never agree on the labels, so it sort of doesn't matter what you call it. people will argue with you anyway.

#

@weary garden for when you get around to it: https://en.wikipedia.org/wiki/Evaluation_strategy

Evaluation strategies are used by programming languages to determine two thingsβ€”when to evaluate the arguments of a function call and what kind of value to pass to the function.
To illustrate, a function application may evaluate the argument before evaluating the function's body and pass the ability to look up the argument's current value and mo...

weary garden
#

I already differentiate between an argument and a parameter

spark magnet
#

right: parameter is in the function definition, argument is in the call.

#

@fiery path what is your talk for PyCon?

weary garden
#

as far as Java is concerned it is often claimed that Java doesn't have pointers when the reality is that everything is a pointer in Java as evidenced by the existence of NullPointerException.

sacred yew
#

npe is kind of a bad name

#

but just because it's implemented with pointers doesn't mean it is a pointer

weary garden
#

semantics.

spark magnet
wide shuttle
sacred yew
#

C++ references are different from c++ pointers

#

you don't call a "reference" a "pointer" in c++

spark magnet
sacred yew
#

same concept

weary garden
#

indeed, my first instinct is to base the naming of my semantic concepts on the C++ model even though neos itself is supposed to be language agnostic.

sacred yew
#

but c++ refs are different from other langs

weary garden
#

the problem is the same names being used for different things at different levels of abstraction

spark magnet
spark magnet
raven ridge
#

I can't think of any other language with pass-by-reference that matches how C++ does it. I'm sure there are some, I doubt C++ invented it, but it's certainly not common.

weary garden
#

I might have langauge.static.reference and language.dynamic.reference

sacred yew
#

but java follows python semantics

#

anf java isn't dynamic

spark magnet
#

true dat

weary garden
#

where language.static.reference is effectively an immutable pointer

spark magnet
weary garden
#

well maybe language.reference.dynamic given that static languages can have non C++esque references

sacred yew
#

I'm using the def of "each variable has a specified type" here for dynamic, idk how youre interpreting it

#

*static

weary garden
#

possibly language.gc.reference

sacred yew
#

there's definitely a non-gc lang that follows pyyhon-style

weary garden
#

yes, naming things is a hard problem

#

OK, I think I will settle on language.pointer language.mutable_reference and language.immutable_reference

sacred yew
#

personally I would do something like
"Pass by copying" for c-style value copying
"Pass-by-assignment" for python style
"Pass-by-reference" for c++ refs

#

wb c primitives

weary garden
#

well these concepts aren't just involved in argument passing

#

what's the difference between "pass by copying" and "pass by assignment"?

raven ridge
weary garden
#

a mutable reference can be reseated

#

C++ reference would be immutable

raven ridge
#

So that's the one that Python has, not the one C++ has?

#

Assignments to references in C++ trigger operator= and mutate the object. So in that sense, the reference type that you're calling "immutable reference" has the behavioral difference of being usable to mutate the object by assigning to them

weary garden
#

true

#

ok, language.pointer and language.reference_pointer πŸ˜„

raven ridge
#

Meanwhile, the references in Python, which you're calling mutable, can be rebound, and can mutate the passed object, but cannot rebind the passed object

weary garden
#

yeah I am happy with that. pointer and reference_pointer.

limpid marten
#

It would be interesting if you could override the assignment operator in Python, you might be able to "mimic" reference types in some horrible way.

raven ridge
#

Except that would break the entire language πŸ™‚

limpid marten
#

Hahaha certainly.

weary garden
#

language.gc.reference is language.reference_pointer

#

language.reference is akin to a C++ reference

#

AKA an alias

#

language.reference_pointer is a pointer you can change the address of but cannot manipulate like a C pointer

limpid marten
#

Is there a way people annotate function arguments in Python to say that they treat it as const?

#

I've looked through typing and can't seem to find anything, but feel it would be useful at least for documentation's sake?

#

Am I perhaps missing something?

raven ridge
#

When you say "const", do you mean that the argument won't be reassigned, or that the object referred to by the argument won't be modified through that reference?

limpid marten
#

The latter.

#

Treated as if it were immutable I guess.

unkempt rock
raven ridge
grave jolt
sacred yew
#

sighs

#

java mostly isn't dynamic

vale flax
#

as long as you put things in the right box, you've got dynamicism!

boreal umbra
#

I forgot--how are chained comparison operations evaluated again? Given a, b, c, where those are each numpy arrays of the same shape, a == b == c has the intended semantics if it's evaluated as (a == b) == (b == c) (I think), but I don't think that holds up for lt, gt, le, or ge.

modern night
#
>>> import numpy as np
>>> a = np.array([1, 2, 3])
>>> b = np.array([1, 2, 3])
>>> c = np.array([1, 2, 3])
>>> a == b == c
Traceback (most recent call last):
  File "<pyshell#4>", line 1, in <module>
    a == b == c
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()```
boreal umbra
#

!e

import numpy as np
val = np.array([1, 2, 3]) == np.array([1, 2, 3]) == np.array([1, 2, 3])
print(val)
fallen slateBOT
#

@boreal umbra :x: Your eval job has completed with return code 1.

001 | Traceback (most recent call last):
002 |   File "<string>", line 2, in <module>
003 | ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
boreal umbra
#

I see. For some reason I thought chained comparisons were possible with numpy

#

Though I do realize you could do it manually with bitwise operators.

grave jolt
#

(a == b) == (b == c) doesn't make much sense

boreal umbra
grave jolt
#

uhh

#

it's literally the same thing

boreal umbra
#

because those return boolean arrays

grave jolt
#

a == b == c is equivalent to a == b and b == c in any case.

boreal umbra
#

What experience do you have with numpy?

grave jolt
#

...

#

!e

import dis

def f():
    return a == b == c

dis.dis(f)
fallen slateBOT
#

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

001 |   4           0 LOAD_GLOBAL              0 (a)
002 |               2 LOAD_GLOBAL              1 (b)
003 |               4 DUP_TOP
004 |               6 ROT_THREE
005 |               8 COMPARE_OP               2 (==)
006 |              10 JUMP_IF_FALSE_OR_POP    18
007 |              12 LOAD_GLOBAL              2 (c)
008 |              14 COMPARE_OP               2 (==)
009 |              16 RETURN_VALUE
010 |         >>   18 ROT_TWO
011 |              20 POP_TOP
... (truncated - too many lines)

Full output: https://paste.pythondiscord.com/jireqogaro.txt

grave jolt
#

It literally doesn't matter what objects those are.

boreal umbra
#

But numpy doesn't use those operators to return bools

grave jolt
#

and?

boreal umbra
#

if I did a == b == c with numpy arrays, I wouldn't want an instance of bool at the end. I would want an array of bools.

#

but if internally it's doing what the and and or operators do, that won't work

grave jolt
#

and doesn't make a bool

boreal umbra
#

!e

import numpy as np
val = np.array([1, 2, 3]) and np.array([1, 2, 3])
print(val)
fallen slateBOT
#

@boreal umbra :x: Your eval job has completed with return code 1.

001 | Traceback (most recent call last):
002 |   File "<string>", line 2, in <module>
003 | ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
grave jolt
#

!e

class Foo:
    def __init__(self, label):
        self.label = label

    def __eq__(self, other):
        rv = Foo(self.label + other.label)
        print(f"== is called on {self} and {other}, returning {rv}")
        return rv

    def __bool__(self):
        print(f"__bool__ is called on {self}")
        return True

    def __repr__(self):
        return f"Foo({self.label!r})"

Foo("a") == Foo("b") == Foo("c")
fallen slateBOT
#

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

001 | == is called on Foo('a') and Foo('b'), returning Foo('ab')
002 | __bool__ is called on Foo('ab')
003 | == is called on Foo('b') and Foo('c'), returning Foo('bc')
grave jolt
#

Compare two arrays elementwise?

boreal umbra
grave jolt
#

!e

import numpy as np
val = np.array([1, 2, 3])
print(val == np.array([1, 2, 3]))
print(val == np.array([2, 3, 4]))
fallen slateBOT
#

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

001 | [ True  True  True]
002 | [False False False]
boreal umbra
#

because if it internally breaks that statement down into (a == b) and (b == c) (which I learned here just now), I don't believe that would work because of what it appears np.array.__bool__ is doing

grave jolt
boreal umbra
#

Right

grave jolt
#

So your question is how to get an array of truth values where all three arrays are equal to each other?

boreal umbra
grave jolt
#

I'm not sure what your question is about now

boreal umbra
#

My original question was "how can a == b == c evaluate to a boolean array?" and the answer is "it can't"

grave jolt
#

right

#

sorry, I misunderstood you then

#

(but a == b == c really does mean a == b and b ==c)

boreal umbra
#

when comparison operators uphold the expectation that they return a single instance of bool, yes

#

(or they have implicit truthy/falsey values that "make sense")

grave jolt
#

I'm not sure what you're saying

boreal umbra
#

it's not really that important

grave jolt
#

!e

class Foo:
    def __init__(self, label):
        self.label = label

    def __eq__(self, other):
        rv = Foo(self.label + other.label)
        print(f"== is called on {self} and {other}, returning {rv}")
        return rv

    def __bool__(self):
        print(f"__bool__ is called on {self}")
        return self.label != "cd"

    def __repr__(self):
        return f"Foo({self.label!r})"

Foo("a") == Foo("b") == Foo("c") == Foo("d") == Foo("e")
fallen slateBOT
#

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

001 | == is called on Foo('a') and Foo('b'), returning Foo('ab')
002 | __bool__ is called on Foo('ab')
003 | == is called on Foo('b') and Foo('c'), returning Foo('bc')
004 | __bool__ is called on Foo('bc')
005 | == is called on Foo('c') and Foo('d'), returning Foo('cd')
006 | __bool__ is called on Foo('cd')
grave jolt
#

As you can see, first it takes Foo("a") == Foo("b"), which returns Foo("ab"), which is truthy.
Then it takes Foo("b") == Foo("c"), which returns Foo("bc"), which is truthy.
Then it takes Foo("c") == Foo("d"), which returns Foo("cd"), which is falsey, so evaluation is stopped.

boreal umbra
#

!e

import numpy as np
bool(np.array([1, 2, 3]))
fallen slateBOT
#

@boreal umbra :x: Your eval job has completed with return code 1.

001 | Traceback (most recent call last):
002 |   File "<string>", line 2, in <module>
003 | ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
boreal umbra
#

Numpy deliberately sabotages efforts to get the implicit boolean value of arrays.

grave jolt
#

I know

#

well, when bool(a == b) raises an exception, a == b == c is the same as a == b and b == c as well πŸ™‚

pliant tusk
#

I dislike that numpy deliberately breaks bool(array) is there any reason that they didn't want it to behave like lists?

boreal umbra
wide shuttle
boreal umbra
#

That is, having a non-zero number of elements?

wide shuttle
#

Yes

boreal umbra
#

What about np.array([[], []])?

wide shuttle
#

What would you get if you ask for the size of that array?

boreal umbra
#

(2,0), I think?

wide shuttle
#

Those are the dimensions

boreal umbra
#

right

#

strictly speaking the outer-most dimension has two elements

wide shuttle
#

Numpy will say the size is 0

#

!e

import numpy

a = numpy.array([[], []])
print(a.size)
fallen slateBOT
#

@wide shuttle :white_check_mark: Your eval job has completed with return code 0.

0
wide shuttle
#

Since that's the definition of size in numpy arrays, I'd say the analogy with a list would be to be falsy. But it does get muddy.

#

The exception above hints at another interpretation though

#

Whether or not any or all elements are truthy, which is different from the interpretation of the truthiness of a list.

#

I think the numpy way is fine. It makes sense in the context.

boreal umbra
#

!e

import numpy
print(len(numpy.array([[], []])))
fallen slateBOT
#

@boreal umbra :white_check_mark: Your eval job has completed with return code 0.

2
wide shuttle
#

Alright

boreal umbra
#

I think typically, the contract is that objects that implement __len__ should be truthy if the length is > 0

#

though numpy arrays (and objects that use the data model similarly) have their reasons for being an exception

wide shuttle
#

That means that we can have a consistent bool for numpy arrays as well

boreal umbra
#

For the record, I'm not necessarily arguing that the designers of numpy were correct to make the design choice that they made. Or that some other design would be better.

grave jolt
#

I think this brings back to the discussion we had earlier, about conversion to bool and that not everything should be allowed to be treated as a boolean
@gleaming rover @raven ridge

#

The numpy behaviour seems correct to me, whether a numpy array is empty is slightly ambiguous

#

but isn't it also kinda ambiguous whether [[], [], []] is 'empty'?

livid dove
#

why should that be empty

#

its a list filled with three empty lists

#

just because the glasses are empty doesnt mean that the cabinet is empty

raven ridge
#

Lists are mutable - if we accepted a definition where [] was empty but [[]] was not empty, wouldn't it be odd that putting an empty list into an empty list resulted in an empty list of length 1?

livid dove
raven ridge
#

er, yes, I did make a typo

#

if we accept that [] and [[]] are both empty, then there would be empty lists of length 1

#

or 2 or 3 or whatever. Or arbitrarily nested, for that matter. [[[]]] would still be empty.

grave jolt
#

It depends on what the list represents. If a list represents e.g. a game board, [[], [], []] might semantically be empty.

#

but I understand that a list being empty has a clear meaning in Python

#

or to take the numpy example -- [False, False, False] might mean an empty/falsey result, so you have to use all or any in that case

raven ridge
#

Yeah. I can see how it could have been defined that way. There's a very good reason not to define it that way, though - that cannot be implemented efficiently

livid dove
#

god all this theoretical programming talk

#

not really my cuppa tea but interesting nonetheless

raven ridge
livid dove
#

well im more practically oriented

rich cradle
livid dove
#

if it works dont touch it, thats my philosophy

astral gazelle
#

This is more of a "it works, but how"

grave jolt
raven ridge
#

Well, not really - it just means that they have to pick one thing for bool(x) to mean

grave jolt
#

right

raven ridge
#

in numpy they decided not to pick, which is in itself a thing they could pick

#

they picked "it's meaningless" - or ambiguous, I guess.

grave jolt
#

yeah, IIRC we agreed that objects shouldn't define __bool__ by default

raven ridge
#

yeah.

#

remember, though, that back in the Python 2 days, there was no __bool__, there was only __nonzero__

#

and from that point of view, it's clearer how we got where we are

#

"a list is non-zero if it contains more than 0 elements" is pretty clear.

grave jolt
#

"a list is non-zero because it's the neutral element with respect to addition"

#

πŸ™‚

raven ridge
#

yeah, that's actually probably more accurate.

#

not a way I would have thought of it, but it makes sense as well.

grave jolt
#

well, it sort of makes sense on values that define +

#

0, "", [], ()

#

also False with respect to |/or

#

and {} with respect to |

#

and {*()} with respect to |

raven ridge
#

and bytearray() and bytes() with respect to +

grave jolt
#

yeah

obsidian igloo
#

Can you compile python, to native performance?

flat gazelle
#

Not all of python, but numba can compile some of python to native performance

boreal umbra
flat gazelle
#

native performance being as fast as the functionally equivalent code in C

wide storm
#

heyy can anyone say how to make a modmail bot from discord.py?

flat gazelle
#

numba doesn't compile python, but a specific subset of python it can compile effectively

flat gazelle
wide storm
#

ohh sorry, actually i am new so got a little confused

flat gazelle
#

notably heterogenous lists and dicts, as well as non-list comprehensions

unkempt rock
#

... you can use pypy it is much fastesr than py ...

#

I use pyinstaller to compiler little scripts, beware of your antivirus it often flags some of those .exe has bad virus, at least my company antivirus ...

radiant fulcrum
#

PyInstaller doesnt compile so much as wrap

#

It wraps the interpreter and code together which is what sets the whole AV off because it looks and behaves like alot of viruses that spawn internal processes

unkempt rock
limpid marten
#

I think if you recompile pyinstaller's bootloader yourself you can avoid that

radiant fulcrum
#

i mean i dont blame them

#

an awful lot script kiddy viruses are made with Pyinstaller which doesnt help

#

but equally the whole system isnt something you'd massively distribute

#

the only distributed Python app ive seen that's wrapped with PyInstaller is Docker's CLI and compose

magic abyss
#

yeah I guess

unkempt rock
#

AFAIK, the PyInstaller trojan positive is only triggered by onefile bundles. The strong point of PyInstaller is the ease of use, things like packaging PyQt into an executable are usually not so easy with other libraries. But there are way more performant alternatives like PyOxidizer, which is also a packager that embeds the Python interpreter, like PyInstaller; or Nuitka, a Python compiler that can build onefile executables.

spark magnet
grizzled grove
hexed maple
#

!e

print(*b'a')
fallen slateBOT
#

You are not allowed to use that command here. Please use the #bot-commands channel instead.

hexed maple
#

it prints 97

#

which is ascii for 'a'

unkempt rock
raven ridge
#

!e ```py
print(*b"abc")

fallen slateBOT
#

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

97 98 99
raven ridge
#

That's because *obj in a function call iterates over the object and passes each element as a seperate argument, and iterating over a byte string gives you the value of each byte as number between 0 and 255.

unkempt rock
smoky bison
raven ridge
#

reversing it means getting back to a bytes object, rather than a str object

pliant tusk
#

bytes([ord('a'), ord('b')])

raven ridge
#

!e ```py
print(bytes([int(num) for num in "97 98 99".split()]))

fallen slateBOT
#

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

b'abc'
devout kelp
#

quick question that doesn't deserve a help channel:
does c++ have a structure similar to python's with open() that doesn't need a .close()?
c++ version:

void readfun() {
    string nyNum;
    ifstream MyReadFile("filename.txt");
    cout << "\nDisplaying lines from a text file. \n";
    while (getline(MyReadFile, myNum)) {cout << myNum;}
    MyReadFile.close()
}

python version:

def readfun():
    print("\nDisplaying lines from a text file. \n")
    with open("textfile.txt","r") as f:
        for line in f: print(line)

Any help would be appreciated, as well as an @ if you could. Thanks!

flat gazelle
#

@devout kelp this would be better suited to an off topic channel. C++ uses destructors for resource management, since they are predictable, unlike python destructors, which can run sometime after the file goes out of scope (though in cpython they are also predictable).

devout kelp
#

thanks, and will do! @flat gazelle

limpid marten
novel vector
#
xored = tuple(int(x) ^ _XOR_MODE_ for y in _bins for x in y if x.isdigit() else x)
``` I have syntax error on that else statement, but I can recall that something similar have worked for me in the past
#

So what is wrong with that else statement, how to fix that?

peak spoke
#

You should ask in a help channel as this channel is not suited for questions like that, but you're trying to use an incomplete conditional expression as python doesn't allow an "else" for the if part of a comprehension

unkempt rock
#

why is list.copy a thing?

#

the language has a dunder for copy.copy

#

just make users use copy.copy instead of having both the dunder and the method?

peak spoke
#

Wouldn't be surprised if the method predates the module

unkempt rock
#

i have never used the method

grave jolt
flat gazelle
#

well, .next got removed.

peak spoke
#

I think deprecations on builtins are a bit more conservative. Was a bit surprised recently when I found out inspect has the getmro function

pliant tusk
#

i doubt any of the (list | dict | set).copy will be removed

unkempt rock
grave jolt
#

but they do the same thing, right?

unkempt rock
#

list, set, dict .copy are much less common to see

unkempt rock
#

what is the recommended thing for user defined mutables?

#

implementing the dunder right?

pliant tusk
# unkempt rock yes

obj.__class__ gives the objects class. type can take 2 different sets of args, a single object, or (str, tuple, mapping) which creates a new class

#

the fact that type(obj) gives the objects class is a special case

unkempt rock
#

oh, is it an implementation detail too though?

pliant tusk
#

no thats a detailed part of the language afaik

pliant tusk
frigid trout
#

Yeah I use dict.copy a lot when iterating over a dict that I'm going to change

raven ridge
# flat gazelle well, .next got removed.

Well, .next probably should have been a dunder all along. It was always meant to be called automatically by the interpreter, not manually by the programmer. And backwards compatibility for that during the 2 to 3 switch was just ```py
next = next

boreal umbra
desert peak
#

is there a good way to communicate unused parameters via * & **? _ is applicable for an argument, but obviously can't be used for both

#

tho I still get whining from IDEs about unused parms with __

charred wagon
#

Good question. I don't know of anything nor do I recall seeing any attempt at that in the wild. I would approach it by either leaving them named as args and kwargs or maybe use a double underscore for one of them.

#

And just ignore the lint error or whatever you're getting.

desert peak
#

isn't _ special cased in the python environment? it seems weird I can't use it for both args

#

I can always throw # noqa at it, but I don't like swallowing errors if I can fix them 😒

charred wagon
#

No it isn't special cased (unlike e.g. C#). It's just a naming convention.

desert peak
#

it usually holds last-evaluation, which means it's somewhat special at least

#

unless that's a REPL trick

raven ridge
#

It's special cased in the repl only.

desert peak
#

ahh

raven ridge
#

And in pattern matching in 3.10, where it will become a special case in the context of a match statement.

desert peak
#

as a catch-all, ye?

raven ridge
#

Yeah.

#

Other than those 2, it's just a regular variable

#

And even in the repl, if you assign to it, it stops being special until you del _

desert peak
#

huh, didn't know that TIL

raven ridge
#

It's because the repl _ special case puts the last expression into __builtins__._

#

So if there's a global or local variable called _, it shadows the built-in.

wide shuttle
#

I'm at least happy that the _ special case in pattern matching doesn't rebind _. That would have been messy with those gettext aliases.

#

There are a few rebinding pitfalls, such as rebinding names of partially matching (non-matching) patterns

#

Which is also implementation dependent (as it's deliberately undefined behavior)

wide shuttle
#

It's not uncommon. C is full of it.

#

It allows implementations to optimize

desert peak
#

that doesn't make it acceptable, however

#

"the way it's always been" is not great thinking 🀷

wide shuttle
#

It's not "the way it's always been"

desert peak
#

"C is full of it" implies that to me

wide shuttle
#

It has a specific reason: It allows implementations to optimize how they handle it

#

C is full of it because such optimizations are historically important in C

desert peak
#

but that's kind of an important behavior to have a distinction around

#

whether a variable is bound or not

wide shuttle
#

Something as simple as evaluation order of the operands is undefined in C

radiant fulcrum
wide shuttle
#

Haha

desert peak
wide shuttle
#

Something as simple as this contains undefined behavior in C:

a = 3;
b = a * a++;
desert peak
#

I'd write Rust if I got paid to do so but my boss worries my coworkers wouldn't be able to learn it (webdev python team)

#

postfix doesn't have priority?

wide shuttle
#

It's because the order of subexpressions isn't defined

raven ridge
#

Reading the same variable twice without a sequence point in between is undefined behavior.

#

And only a specific, small, set of things are sequence points

wide shuttle
#

So, if you have (a + b) * (c - d) either subexpression may be evaluated first

#

So, in the case above, even if we were to write parentheses, (a) * (a++), it matters whether we do the righthand subexpression first or the lefthand one

#

In any case, I was going to share this interesting example of structural patma:

#
a = "some value"

name = (1, 2)

match name:
    case (a, 3):
        print("This does not print, as it doesn't fully match")
    case _:
        print("This does, catch all")


print(a)
#

The first case, (a, 3) is a partial match, with the assigned before the mismatch, which, in the current CPython implementation, will lead to a being reassigned

#

So the last print will print 1 and not "some value"

#

If we were to do this:

a = "some value"

name = (1, 2)

match name:
    case (2, a):
        print("This does not print, as it doesn't fully match")
    case _:
        print("This does, catch all")


print(a)
#

We'd still print "some value"

#

Since our matching steps failed before we reached the a rebinding

#

The specification leaves open that other implementations solve this in a different way, with different rebinding results

radiant fulcrum
#

Just one of the things where i feel the dynamic nature of python makes it so much nicer to work with

autumn nest
#

quick question -- is it uncommon for Python programs to actually use structs to represent data structures? Where somewhere like Rust or Go I'd use a struct, in Python I end up creating a dict with string keys

#

Python is, after all, strongly typed

#

and it would make sense for me to be able to say "here's a type, it composes X and Y and Z fields"

raven ridge
#

Seems like you're describing a class, which are very common.

#

If you mean a plain old data, no extra methods class, there dataclasses

autumn nest
#

the problem with classes in Python is that they are very loose and end up becoming too verbose for the purpose of simply defining a structure

#

i.e. let's imagine a hypothetical

struct Coord {
    x: f64
    y: f64
}
radiant fulcrum
#

In thay case a named tuple is probably what you want

raven ridge
#

Or a dataclass.

radiant fulcrum
#

Either or yeah

autumn nest
#

a "dataclass" sounds semantically relevant

#

I don't remember that being a thing in Python

raven ridge
#

I'd usually prefer dataclass over namedtuple unless you specifically need unpacking/indexing

radiant fulcrum
#

Dataclasses be easily frozen aswell making immutable for the most part

#

And same

#

I use dataclasses way more than named tuples

autumn nest
#
@dataclass
class Coord:
    x: float
    y: float
#

omfg

#

Python statically typed now?!

radiant fulcrum
#

No?

#

Dataclasses basically just remove all of the boiler plate and give you the option to add and remove certain features

autumn nest
#

New in version 3.7.

radiant fulcrum
#

Think of it like a basic derive macro in rust

autumn nest
#

I'm just shocked at the variable type specifier

#

last time I touched Python was 2.7 iirc

radiant fulcrum
#

Thats just typehints

autumn nest
wide shuttle
#

Do mind that it's just a type hint