#internals-and-peps

1 messages Β· Page 107 of 1

halcyon trail
#

so it all works out

paper echo
#

is this not what Protocol does?

#

oh i see

halcyon trail
#

Basically, imagine in python you had a free function:

def add(x: Sequence[T], y: Sequence[T]) -> Sequence[T]:
    ...
paper echo
#

hmm.... if list actually inherited from collections.abc.MutableSequence then you could get closer

halcyon trail
#

Then you can see that it's reasonable that z = add(x, y) will work

#

because x and y are both of type Sequence[Foo]

#

x is a Sequence[Bar1] and by covariance its Sequence[Foo]. same for y.

#

The problem in python is that member functions are just very specifically bound to that type, they can't make any broader assumptions.

#

Basically, imagine in python if there was actually a way to define operator+ on Sequence, instead of on list

#

that's what's happening in Kotlin

#

there's no way to do this in python and that's why this stuff doesn't work out nicely

#

and coincidentally it's the exact same reason why we can't just have operator+ on iterators, and Kotlin can

paper echo
#

yeah makes sense

plush merlin
#

anyone know if there's a way to get an error message out of the discords api "tasks.loop" loop? when it errors it just stops with no message or anything.

#

i have copy pasted this question into a help channel

paper echo
#

Consider a class like the Thing in the example below, and assume there are Good Reasons for doing it this way.

In do_something, the _do_* methods are guarded by the if self.db is None check, in that they can't be executed if the db is None. However, there's no way to signal to the type checker that this should always be true; I either have to add the same if check to each of the _do_* methods, or do a cast which could result in a runtime error if one of these methods happens to be used incorrectly, and would not be caught by the type checker.

Is this even possible without dependent types? How do other languages with nullable-but-null-safe types handles this case?

import sqlite3

class Thing:
    db: Optional[sqlite3.Connection]

    def __init__(self, Optional[sqlite3.Connection]):
        self.db = db

    def _do_insert(self, obj_id):
        self.db.execute(
            'insert into mystuff values (?)',
            (obj_id,)
        )

    def _do_delete(self, obj_id):
        self.db.execute(
            'delete from mystuff where id = ?',
            (obj_id,)
        )

    def do_something(action: str, obj_id: str):
        if self.db is None:
            raise RuntimeError('Cannot do things without a db.')

        if action == 'insert':
            self._do_insert(obj_id)
        elif action == 'delete':
            self._do_delete(obj_id)
        else:
            raise ValueError(f'Unknown action: {action}.')
#

Ideally I'd be able to annotate _do_insert and _do_delete with something stating that "this should never be called if self.db cannot be proven as non-None"

#

https://kotlinlang.org/docs/null-safety.html it looks like the Kotlin ?. operator lets you write self.db?.execute which is null-safe in that it will be a no-op (return null without taking any action), and the compiler will complain if you don't, but you can't tell the compiler to "never let me call self._do_delete if self.db is None".

Kotlin Help
undone hare
#

Hey @fresh willow, please don't post random memes on the server

#

Neither you @stoic locust

halcyon trail
#

@paper echo i'm not sure I fully understand what it is that you want

#

Oh, maybe I better understand

#

So, in Kotlin for example, you would actually define these as extension functions on self.db

#

in which case, as you say, self.db._do_insert is a compilation error

#

self.db?._do_insert will be a no-op if it's null, but that's what ?. is, it's an explicit request to do exactly that

#

not letting people calling methods with ?. because of nullability makes no sense

#

Really though the basic idea of this solution applies here as well. You just need to make _do_insert and _do_delete static methods instead, that take an sqlite3.Connection

#

instead of member functions taking self

#

there's no special annotation. By taking self, you've already accepted that you're taking an Optional[Connection]. So you have to stop taking self, and instead take a Connection

flat gazelle
#

the best solution is generally raii, where the object acquires what it needs in __init__. It doesn't work as well with async, but it is still viable

halcyon trail
#

this seems pretty orthogonal to the problem here, tbh, even supposing that python has RAII, which in itself is a bit of a stretch

#

We don't know why the database is Optional here, it could be for reasons unrelated to resource management

flat gazelle
#

well, an object that needs a database ideally shouldn't have it nullable. I am not aware of a language that solves this nicely. The better solution would just be taking as an argument, yeah.

halcyon trail
#

There's also trade-offs between Thing opening a database connection itself, and dependency injection, where Thing just receives some object that provides a suitable API, which one makes sense requires more context than has been given (and again, isn't really part of the main problem here)

#

Well, then you're basically saying that the argument shouldn't be nullable, but salt asked us to accept that there are good reasons for that

#

I guess we could probe into why exactly its nullable

paper echo
flat gazelle
#

you can make indefensible claims to the type checker using assert

halcyon trail
#

You don't really need to separate it, you can take multiple arguments in your static functions

paper echo
#

yeah, like self i suppose πŸ˜›

halcyon trail
#

Well, with self you are committing to something specific though, right

#

in fairness, you could even still take self, but also take a Connection

paper echo
#

in this case it's nullable because the database connection is not always available. i am not actually raising an exception in the real application, i am signaling in-band that the database is missing (HTTP 503)

halcyon trail
#

this is slightly weird because now you have two arguments that alias each other, so its less ideal

#

But, it would still achieve the goal, static type checking wise

#

users would have to come up with something with type Connection to call the member function

fallen slateBOT
#

:incoming_envelope: :ok_hand: applied mute to @unkempt rock until 2021-05-11 20:18 (9 minutes and 59 seconds) (reason: duplicates rule: sent 4 duplicated messages in 10s).

#

:incoming_envelope: :ok_hand: applied mute to @haughty thistle until 2021-05-11 20:18 (9 minutes and 59 seconds) (reason: duplicates rule: sent 4 duplicated messages in 10s).

halcyon trail
#

?

#

what is going on

paper echo
#

kids spamming, nothing to see here

halcyon trail
#
    def _do_delete(self, obj_id, db: Connection):
        db.execute(
            'delete from mystuff where id = ?',
            (obj_id,)
        )
#

assuming that in the real code you have other uses of self

#

Again, I'd rather make it static and take all arguments explicitly; if you have so many arguments then it's probably a sign your class/functions are too big

#

but it's a reasonable quick solution if you don't want to refactor as much

#

The other option is to make it not Nullable. If the database is not available, then you provide a NullConnection which either does nothing or throws in its API, whichever makes sense.
Which of these two makes more sense, again just depends on a lot of things.

paper echo
#
import sqlite3

def _do_insert(thing: Thing, db: sqlite3.Connection, obj_id: str) -> None:
    db.execute(
        'insert into mystuff values (?)',
        (obj_id,)
    )

def _do_delete(thing: Thing, db: sqlite3.Connection, obj_id: str) -> None:
    db.execute(
        'delete from mystuff where id = ?',
        (obj_id,)
    )

class Thing:
    db: Optional[sqlite3.Connection]

    def __init__(self, Optional[sqlite3.Connection]):
        self.db = db

    def do_something(action: str, obj_id: str):
        if self.db is None:
            raise RuntimeError('Cannot do things without a db.')

        db = self.db

        if action == 'insert':
            handler = _do_insert
        elif action == 'delete':
            handler = _do_delete
        else:
            raise ValueError(f'Unknown action: {action}.')

        handler(self, db, obj_id)
#

something like this, then, for the "static" approach?

halcyon trail
#

if you're taking Thing there's literally no point making it "static"

#

the point of make it a static or free function is to not take Thing

#

I assume that Thing has more fields, right, not just db

flat gazelle
#

well, if the db connection is never None in that function, you can do

    def _do_delete(self, obj_id):
        assert self.db is not None
        self.db.execute(
            'delete from mystuff where id = ?',
            (obj_id,)
        )
``` and the static checker will believe you
#

at least mypy uses that

paper echo
#

yeah, specifically i would be calling other Thing instance methods before and after the db.execute

paper echo
halcyon trail
#

Well, it doesn't actually solve your original problem though...

#

you want mypy to try to prevent you from calling _do_delete in the first place

paper echo
#

yep. it's still "duplicating logic" in a way that i wish i didn't have to do

halcyon trail
#

unless you prove that self.db is not None

flat gazelle
#

I think rust can actually do this using some odd features. But that is about the only language with some idea of object states I know of.

paper echo
#

in the not-really-static version i showed above, you're basically using the type signatures to do the assertion, and which lets you re-use the proof that was constructed in do_something

halcyon trail
#
class Thing:
    db: Optional[sqlite3.Connection]
    x: int

    def __init__(self, db: Optional[sqlite3.Connection], x: int):
        self.db = db
        self.x = x

    @staticmethod
    def _do_insert(x: int, db: sqlite3.Connection, obj_id: str):
        # Type checkign works fine now because db is not optional
        ...
    @staticmethod
    def _do_delete(x: int, db: sqlite3.Connection, obj_id: str):
        # Type checkign works fine now because db is not optional
        ...

    def do_something(action: str, obj_id: str):
        if self.db is None:
            raise RuntimeError('Cannot do things without a db.')

        db = self.db

        if action == 'insert':
            handler = self._do_insert
        elif action == 'delete':
            handler = self._do_delete
        else:
            raise ValueError(f'Unknown action: {action}.')

        # This type checks too, because you would have thrown earlier if self.db was None
        handler(self.x, db, obj_id)
#

yes, type signatures are generally how you want to propagate information about Optional πŸ™‚

#

that's the whole point right

paper echo
#

(you an add py or python after the ``` to get syntax highlighting)

halcyon trail
#

In the example I posted, if you didn't have the check and raise, then mypy would complain

#

because the type of handler would be a callable that is not compatible with the call that you made

paper echo
#

yeah

#

same idea as mine basically

#

rather, mine is the same as yours

halcyon trail
#

Well, yes, except your argument takes thing, at which point, like I said, you may as well make it a member function πŸ™‚

#

your functions, sorry

paper echo
#

right. in either case, you can still mistakenly access thing.db, instead of using the db parameter, but at least the type checker will identify the former case correctly as maybe calling None.execute

halcyon trail
#

It's less desirable to pass Thing because when you call a function you don't really want to pass the same argument twice, even if indirectly

#

No, in my case there's no thing argument

#

so you can't

paper echo
#

i meant to delete the "in either case"

halcyon trail
#

(in _do_insert)

#

ah

paper echo
#

yes, you're right

halcyon trail
#

Yeah. If you are already requiring a non-nullable db, then passing a nullable one as well is less optimal, which is why it's better to not pass Thing

paper echo
#

that makes sense. in my particular case i'd have to further refactor code around it so i'm willing to accept the localized weirdness

halcyon trail
#

but obviously if you have tons and tons and tons of properties in Thing, and you need to use many of them in _do_delete, you may still want to pass it

#

Yes. Although, again, if you are doing that, just leave them as member functions

#
    def _do_delete(self, db: sqlite3.Connection, obj_id: str):
        # Type checkign works fine now because db is not optional
        ...
#

No point making a function static just to pass self anyway

paper echo
#

yeah, that's fine too

#

this is helpful, thank you

placid kraken
#

is there any way to set a default value (eg. argument = "nice") and required type (eg. argument: str) to an argument

paper echo
#

the NullConnection thing is interesting too, could be useful in other cases (or i could try/except around the errors that it raises internally - but that's suspiciously like Using Exceptions For Control Flow)

#

@placid kraken argument: str = "nice"

placid kraken
#

thanks

halcyon trail
#

yeah, I mean the NullConnection would be useful depending on what your plan is

#

what does Thing actually plan to do if db is None and some method that requires not-null db is actually called

#

if it's always going to throw, then you may as well just have a NullConnection that always throws and get rid of the Optional

#

it's the exact same thing with less boilerplate in Thing, which is a win IMHO

#

If you're going to try to do nothing without failing, e.g. always do nothing, returning empty collections, etc, whatever, then that can also be handled by a NullConnection idea (just a different implementation)

#

and again you remove boilerplate and improve modularity/encapsulation

#

The less Thing knows the better. So in the end you have to evaluate if ther eis really a reason that Thing needs to know about having an Optional[Connection]. Or can everything simply be handled by having the correct Connection implementation.

paper echo
#

I like the NullConnection strategy for a client library, e.g. the Python GraphQL library gql which lets you inject a "transport" that determines how to send the requests (async or sync). Something like a NullTransport in this case would make sense to me.

#

however if the public Transport interface is sufficiently complicated, the implementation of NullTransport could end up really verbose or really ugly (eg. using __getattr__)

#

i will move the rest of my question to #software-architecture , i think this is getting away from "advanced" typing stuff and more into mundane territory

neon mesa
#

love the pfp quick πŸ™‚

halcyon trail
#

pfp?

#

@neon mesa

#

oh, profile picture. Thanks πŸ™‚ I need to trim it, there's some funny whitespace at the top and bottom

static bluff
#

Anyone have anything interesting to share?

native flame
#
>>> tup = ([1],)

>>> tup[0] += [5]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> tup
([1, 5],)        # Errors, but 5 still gets appended

>>> tup[0] = tup[0] + [6]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> tup
([1, 5],)

>>> tup[0].extend([6])
>>> tup
([1, 5, 6],)

the second one or the third one don't really surprise me
But for the first one, I discovered that x += y isn't just x = x.__iadd__(y), it's more like:

temp = x.__iadd__(y)
x = temp

Why is it this way though?

paper echo
#

!e ```python
tup = ([1],)
try:
tup[0] += [5]
except Exception as e:
print(e)
finally:
print(tup)

fallen slateBOT
#

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

001 | 'tuple' object does not support item assignment
002 | ([1, 5],)
paper echo
#

no kidding

#

this seems like a bug to me

#

much worse than the "unbound local" gotcha

feral cedar
#

hm, i don't think it is a bug

grave jolt
#

I've seen this a while ago. This is not a bug

native flame
#

If you follow it along

temp = x.__iadd__(y)
x = temp

then it makes sense that the list gets modified but then the tup[0] = temp part fails

paper echo
#

that makes sense

#

naively i would have expected += to be applied to "the thing referred to by tup[0]"

#

so yeah this is on par with the "unbound local" gotcha

#

!e ```python
x = 3
def f():
x = x + 10
return x
f()

fallen slateBOT
#

@paper echo :x: Your eval job has completed with return code 1.

001 | Traceback (most recent call last):
002 |   File "<string>", line 5, in <module>
003 |   File "<string>", line 3, in f
004 | UnboundLocalError: local variable 'x' referenced before assignment
paper echo
#

that's always a fun one to explain in a help channel

feral cedar
#

i don't actually know the reason for that

grave jolt
#

The semantics of EXPR += y are:

x = EXPR
new = EXPR.__iadd__(y)
EXPR = new

so yeah, this makes sense.

#

Although I totally agree that x += y being distinct from x = x + y (and especially being .extend on lists) is cursed

paper echo
#

i think the idea is that because x is on the left hand side, cpython creates a binding for x local to f, but without anything bound to it yet, so referring to it in the rhs refers to the unbound local binding x

#

!e ```python
import dis
x = 3
def f():
x = x + 10
return x
dis.dis(f)

fallen slateBOT
#

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

001 |   4           0 LOAD_FAST                0 (x)
002 |               2 LOAD_CONST               1 (10)
003 |               4 BINARY_ADD
004 |               6 STORE_FAST               0 (x)
005 | 
006 |   5           8 LOAD_FAST                0 (x)
007 |              10 RETURN_VALUE
grave jolt
#

Yes, an assignment will never work on a global unless you say global x

paper echo
#

not very enlightening but i guess it's just a name collision

#

well the idea would be to assign global:x + 10 -> local:x

grave jolt
#

ah

#

x = globals()['x'] + 10

#

ez lemon_smug

paper echo
feral cedar
grave jolt
#

a single name cannot be both global and local in the same scope

paper echo
grave jolt
#

ahem, clean up perhaps

#

OSBRN, I'm translating the official Python tutorial now, and it's somewhat shocking how technical and fast-paced it is.
If I started with it as my first Python resource, I would be totally blown away by all the jargon

paper echo
#

i think i remember a similar experience

#

a lot of python docs are like that, clearly written by people who eat sleep and breathe python and whose version of "obvious" is not shared by the rest of us

feral cedar
grave jolt
#

On a separate but related note

#

I made up this abbreviation yesterday

paper echo
#

its not bad

#

OSBRN not that hard to type on a us qwerty keyboard

grave jolt
paper echo
#

has a 2003 flavor

grave jolt
paper echo
#

sounds like a "python for java users" tutorial

grave jolt
#

(For C++ programmers: all methods in Python are effectively virtual.)
well, yeah

#

I suppose this part was written before Java existed lol

paper echo
#

actually this looks really useful for the people who wander into python world from c++, java, etc. and wonder why scoping in classes is so weird

grave jolt
feral cedar
#

wait actually?

grave jolt
#

yeah

#

well, there have been some minor changes, like e.g. -> for example

#

but no more

#

so while it's technically older than Java, C++ was probably way more popular and familiar

#

well, that's one hell of a journey to the past

halcyon trail
#

I dunno, I think it's reasonable for language docs to just explain syntax for relatively basic and common things

#

Most language docs I have seen don't explain what an if statement does in detail, or a loop

#

They just say "here's the syntax" and if you don't already know what these things are, yes it'll be confusing

#

But if you don't know what inheritance is the official python docs probably shouldn't be your first stop

iron onyx
#

Hello

#

Does anyone here can help me with my python task ? Simply pm me . It's much appreciated if you can lend me a hand ^^

raven ridge
grave jolt
#

yeah, it just seems like the tutorial is aimed at people who're already programmers.

#

specifically C/C++ programmers πŸ˜„

raven ridge
#

It would need to be much larger to cover everything beginners need to know at the rate they can digest it

#

That's not to say it can't be spruced up, or that it shouldn't introduce concepts before talking about how they're applied, just that it also shouldn't spoon-feed like a tutorial aimed at teaching beginners their first language should.

grave jolt
#

yes, it probably shouldn't be a programming tutorial in general

#

but some parts are a little bit too technical to understand for someone who doesn't already know what's being explained

acoustic crater
#

is there any way to mangle a name differently than with the word after the class keyword?

charred wagon
#

Might be possible by overriding getattr and setattr dunders

acoustic crater
#

upon investigating a bit, I think the actual attribute name is set by interpreter with the word after class keyword

#

but yeah some trickery could change how they are found

#

I only ask cuz my current instructor makes us use mangling for (sic) "private" attributes which are used by subclasses and it leads to all sorts of ugly code to allow for inheritance

#

so I kinda wanna do something rly cheeky but it seems like the interpreter sets the mangling based on which class block the attribute is defined in so I have to limit my cheekiness

prime estuary
#

It's done at compile time indeed.

magic python
#
In [9]: class C1:
   ...:     def __init__(self):
   ...:         self.x = 1
   ...:         self.y = 2
   ...:

In [10]: class C2:
    ...:     x = 1
    ...:     y = 2
    ...:

In [11]: c1 = C1()

In [12]: c2 = C2()

In [13]: c1.x
Out[13]: 1

In [14]: c2.x
Out[14]: 1

is there ever a case where C1 would be a preferable approach to that of C2 ? It seems redundant if there isn't an input for x,y

native flame
#

though the example here is just ints, mutable defaults is the first drawback that comes to mind for C2

#
In [1]: class A:
   ...:     a = []
   ...:     def test(self):
   ...:         self.a.append(5)
   ...:

In [2]: ob1 = A()

In [3]: ob2 = A()

In [4]: ob1.a, ob2.a
Out[4]: ([], [])

In [5]: ob1.test()

In [6]: ob1.a, ob2.a
Out[6]: ([5], [5])
magic python
#

hrm πŸ€”

#

fair - so you'd want to have an init if it's relating to a specific instance of a class rather than across all classes of that type, make sense

sacred tinsel
#

In C1 the attrs live on the instance, in C2 on the class, so whichever makes more sense would be preferable

feral cedar
#

in other languages they would be called "static" variables

magic python
#

I'm sometimes not too sure what to do when i have "class constants"< like if you have:

class C3:
    def __init__(self, y):
        self.a = 1
        self.y = y + self.a

is that the "right" approach?

#

here a isn't expected to change, and would want to be the same across all instances created of class C3

sacred tinsel
#

Using the definition of C2 from your snippet, we do get this behaviour:

>>> a, b = C2(), C2()
>>> a.x += 1
>>> a.x
2
>>> b.x
1

What happens here (please fact check me) is that x lives on the class, but a.x += 1 reassigns on the instance, so now C1 has an instance attribute called x that gets found first in the resolution order, whereas C2 doesn't have the attribute and looks into the class namespace as a fallback

#

It would be a different case if x was mutable and we didn't reassign

#

In which case the change would also show up on b, because both a.x and b.x would still refer to the same object

magic python
#

x is referring to the class attribute (?) rather than the type (as int is immutable)

sacred tinsel
#

Here's a better demonstration of what I mean:

>>> a, b = C2(), C2()
>>> a.x is b.x
True
>>> a.x += 1
>>> a.x is b.x
False
#

b.x still resolves to the attribute on the class, whereas a.x now resolves to the attribute on the instance

#

because the instance namespace is checked first

magic python
#

i need to run through this a bit more - what're your thoughts about C3 though? re the constant thing

#

that's something I've been uncertain of before - I've felt (for some reason) like I should put them out of the init lol

peak spoke
#

Constants being behind the class is fairly common, but there are considerations to both class and instance variables if you also need to reassign them as they will behave differently

sacred tinsel
#

One more even better example (I think):

>>> a = C2()
>>> a.x += 1
>>> a.x
2
>>> type(a).x
1
#

So I'd consider this dangerous if your intention is to have "default" attributes, we only get away with it here because of the immutability (so re-assignment is necessary)

magic python
peak spoke
#

Living in the class object instead of the instances

magic python
#

so like

class C:
    # in class ob
    a = 1
    def __init__(self, x):
        #Β in instance
        self.x = x
#

but that's different @peak spoke , as in my example i wanted to use a's value within the __init__

sacred tinsel
#

Now a will live on the class (so "shared" by all instances, if they access it), whereas x will live on each instance

#

I'm not sure whether it's the right approach because I'm not sure what you're trying to do πŸ˜„

magic python
sacred tinsel
#

You would do self.x = x + self.a or C.a

feral cedar
#

you would need C.a

magic python
#

as it seemed natural to have all values which weren't passed into the __init__ to be outside of it - not sure why, so that's what that question is about - presumably I'd just have:

self.a = 1
self.x = x + self.a

instead - though if it's typed out of order it'd be weird.
@feral cedar this the recommended approach to this then?

feral cedar
#

idk what you mean by "out of order", but you are creating it each time you make an instance

magic python
feral cedar
#

yes in my opinion. i have no idea if that's actually preferred though

sacred tinsel
#

In the example a is not held by all instance though, only by the class

#

By the way, there's also the option of putting the constant in the module namespace

#

If it's internal-use only, I think that's often the best option

#

The difference between self.a and C.a is that if an instance happens to have an attribute named a, it will access that instead

magic python
sacred tinsel
#

For C.a, you will always access the class attribute

#

Yeah, I'd do that

#

Unless there's a reason for A to be accessible on the class

magic python
#

i appreciate this is vague - basically if context is useful - i have some path which is like data/project_1 and there are multiple directories within there - so the metadata for project_1 can have attribute data_root = pathlib.Path('./data/project_1')

peak spoke
#

It depends on the actual use but I usually prefer for constants only used in the class to also be behind its namespace to keep its scope clear. The usual exception for me is if it's something that may want to get edited as some sort of configuration

sacred tinsel
#

Yeah it prob depends on the context too much.. if I had a module with 1 class and 1 path constant used by the class, I'd probably put the constant in the module next to the class

#

if I had a module with 2 classes each using its own path constant, it would prob make sense for the constants to be on the classes

#

fwiw I think I always put constant paths in module namespace, unless they are built dynamically at runtime (rather than import time)

#

I guess import time is run time but you get what I mean πŸ˜„

magic python
sacred tinsel
#

Yea, I think at some point it becomes just personal preference.. someone else may have said that the path should live in some config class elsewhere, if it feels more like config that a constant, etc ..

#

If you're just trying to decide between module/class then pick whichever makes more sense, and if you can't decide just do one and change if it doesn't work πŸ˜„

magic python
#

Lol, yeah, difficult when there are constant deliveries πŸ˜…

lofty rover
#

is it possible to pass only two outputs from 3 from an iterable through to another function?

#

I have an iterable which yields 2 indexes and a label as a result, but I should only pass the indexes to the fuction afterward

flat gazelle
#

itertools.islice is probably what you want

lofty rover
#

sort-of works. If someone with a bunch of iterator experience wants to take a look at #help-orange it'd be greatly appreciated.

hearty phoenix
#

hello

halcyon trail
#

@lofty rover you can write a generator comprehension that only passes the indices

#

Maybe i"m not understanding exactly. but say you have:

x = [(label1, index1a, index1b), (label2, index2a, index2b), ....]

Or rather, that but as an iterator/generator, not as an actual list

#

then

y = ((t[1], t[2]) for t in x)

And now y is a generator that only holds the indices

static bluff
#

You could just use args in the second second function

#
def functionOne(  ):
  return 1, 2, 3

def functionTwo( a, b, *args ):
  ...

functionTwo( *functionOne() )
static bluff
#

So here I am, ticking away on this lexer (it's coming along nicely) and I can't help but think to myself- there has to be a better way

#

It's not too dissimilar from the built in tokenize.py module, and fairly straightforward

#

What it is not is elegant, or readable

#

I've heard of lexer generators in the past and even that they are heavily optimized. Obviously this is a fantastic exercise. Going forward, I absolutely need to have a working understanding of how lexing works, but, is there a sexier solution?

deft pagoda
#

is pythonparser PEG?

#

oh, just the lexer

#

i dunno, most lexers i've seen in the wild have been regex based --- i suppose you could build your own finite state machine if you wanted

neon mesa
#
>>> class A:
...     pass
...
>>> class B(A):
...     pass
...
>>> class C(A, B):
...      def __init__(self):
...          print(type(super()))
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Cannot create a consistent method resolution
order (MRO) for bases A, B
#

wut

static bluff
#

C is trying to inherit from A in two different places

neon mesa
#

i thought this was allowed in python tho

deft pagoda
#

just inherit from B

static bluff
#

I'm also curious though, why does this screw with the mro?

radiant fulcrum
#

Inherits A, then Inherits B, that inherits A again

static bluff
#

I mean, the whole point of an mro is to linearize situations like this

#

So is there some sort of explicit ambiguity? I mean, there must be

deft pagoda
#

you can do it the other way:

In [15]: class A:
    ...:     ...
    ...: 

In [16]: class B(A):
    ...:     ...
    ...: 

In [17]: class C(B, A):
    ...:     ...
    ...: 
neon mesa
#

so you can inherit from the same class multiple times only if it's diamond inheritance?

static bluff
#

That seems a fair assumption

halcyon trail
#

it seems like it doesn't happen for every diamond though

#
In [1]: class A: 
   ...:     pass 
   ...:                                                                                                                                    

In [2]: class B(A): 
   ...:     pass 
   ...:                                                                                                                                    

In [3]: class C(A): 
   ...:     pass 
   ...:                                                                                                                                    

In [4]: class D(B, C): 
   ...:     def __init__(self): 
   ...:         print(type(super())) 
   ...:                                                                                                                                    

In [5]: D()                                                                                                                                
<class 'super'>
Out[5]: <__main__.D at 0x7f3738980070>
#

unless I made some silly mistake

feral cedar
#

A -- B
\ /
C

halcyon trail
#

maybe the problem is that every class needs to be a fixed number of levels from the derived class

#

in an actual diamond, the grandparent is 2 away from the child, both ways

#

in this example, A is both parent and grandparent

#

So an SO suggests that MRO is good because it has the following properties

  • each ancestor class appears exactly once
  • a class always appears before its ancestor ("monotonicity")
  • direct parents of the same class should appear in the same order as they are listed in class definition ("consistent local precedence order")
  • if children of class A always appear before children of class B, then A should appear before B ("consistent extended precedence order")
    In this case you can't satisfy all the properties
#

with the original code, you can't satisfy the second and third

#

that's why you have to switch the order

neon mesa
#

this makes me sad, even tho it's understandable

halcyon trail
#

does it? If you're bumping into these rules frequently then IMHO you're probably using way too much inheritance to begin with

neon mesa
#

oh no this is the first time i saw this, was just trying random stuff

halcyon trail
#

ah ok. Yeah, idk opinions will vary, I don't mind having the option of MI in python, but IMHO should be used carefully and sparingly, and probably avoiding repeated bases entirely

#

there's a reason why so many languages have very deliberately avoided MI entirely

deft pagoda
#

nice for mixins and behaviors -- but no shared bases in these cases

orchid phoenix
#

Can anyone figure out why I'm missing a tab in this output?

#
    address-family ipv6
{% for vtep_short_list_item in vtep_short_list -%}
        no neighbor {{ vtep_short_list[loop.index-1] }}_EVPN activate
{% endfor -%}
!


-=output=-
  address-family ipv6
no neighbor BH-MDF-EDGE01_EVPN activate
no neighbor BH-2-LEAF02_EVPN activate
no neighbor BH-2-LEAF03_EVPN activate```
orchid phoenix
#

@deft pagoda where do you recommend?

#

People in general can't seem to figure it out.

deft pagoda
#

this isn't a help channel

feral cedar
#

is that even python

orchid phoenix
#

python backend Jinja2 for the template

static bluff
#

Sorry Neutralgood- this channel is for the discussion of python's base nature- what makes it tick why it works the way it does. Not for answering specific coding questions

#

You should take your question to one of the normal help channels. Like #help-apple

orchid phoenix
#

ok thank you

grave jolt
#

@vocal gulch please don't post random memes here.

vocal gulch
#

sorry

boreal umbra
#

hashing a string is O(n) for the length of the string, yes? So there's no point caching calls to str.count?

spark magnet
boreal umbra
feral cedar
#

you want to cache calls to str.count?

spark magnet
spark magnet
boreal umbra
#

how could a string cache its own hash?

#

wouldn't you have to hash the string to look it up in the cache's hash table?

feral cedar
#
if stored is None:
  stored = hash(self) #or whatever, pseudopython
  return stored
return stored
```?
spark magnet
boreal umbra
raven ridge
#

A string also caches its UTF-8 encoded representation.

raven ridge
static bluff
raven ridge
#

And that's exactly what they do

#

And why πŸ˜„

prime estuary
fallen slateBOT
#

Objects/unicodeobject.c lines 11794 to 11801

if (_PyUnicode_HASH(self) != -1)
    return _PyUnicode_HASH(self);
if (PyUnicode_READY(self) == -1)
    return -1;

x = _Py_HashBytes(PyUnicode_DATA(self),
                  PyUnicode_GET_LENGTH(self) * PyUnicode_KIND(self));
_PyUnicode_HASH(self) = x;```
static bluff
#

Hey, I've got a curious question for you guys

#

Let's say I'm successful in building this new language. Modified python running in python. Let's say the time comes for me to translate it to C.

#

Cython, if I'm not mistaken, compiles Python into heavily optimized C

acoustic crater
#

only if you're using C types

static bluff
#

Right, okay

#

I'm just curious (which is the whole point of this venture) what I would do if I wanted to fully decouple the project from Python

acoustic crater
#

use a functional language for a compiler lol

static bluff
#

You say that as if it's obvious- I have no context

raven ridge
#

You would write a compiler, or a VM, for your language.

static bluff
#

_> I used to think I was smart/an experienced coder. Not so much after talking with you guys

acoustic crater
#

I suggest just beginning to learn a functional language, if only to learn to apply the concepts inherent to functional programming to python

#

writing a compiler is usually a very late int he game sorta project

#

and just from my cursory foray into haskell everyone constantly says it's great for writing compilers

raven ridge
#

You could write your compiler or VM in any language. You could even write it in your language

acoustic crater
#

compiling is deterministic single input, single output tho so FP seems pretty perfectly suited to it

#

also compiling arbitrarily nested code seemsto require arbitrary recursion in the compiler without rly messy logic

static bluff
#

Alright then. There are things I love about python, some things I don't, and some things from other languages I wish it had. If I wanted to build that, I'd research the grammars and implementations of all the languages involved, using them as blueprints, while ironing out friction points as needed. My VM would tokenize, parse, compile, and interpret the language according to the rules I'd write, while the actual functionality would be written in, most likely, C

raven ridge
acoustic crater
#

but that's just conjuring up the compiler again

raven ridge
#

Not really. Not if you consider thread safety, or the need to expose variables from previously run code

acoustic crater
#

ah so the compiler needs to "decompile" or at least access the namespace as if it's pre compilation

#

but isn't the sorta trivial with python bytecode?

static bluff
#

See, that's what I'm thinking

#

My understanding is that it's all one big pot. The data is all there

acoustic crater
#

but yeah eval exec and other pythonic conveniences will not be easy or even feasible with sky's dreams of executable compilation and runtime static type checking or w/e

static bluff
#

executable compilation?

raven ridge
#

Compiling to bytecode is (relatively) trivial, but that's specifically because there's a VM to execute it. If you want a language without a VM, it would certainly be much tougher

acoustic crater
#

well why decouple if you're gonna reinvent an existing wheel?

static bluff
#

To learn of course!

acoustic crater
#

lol makes sense

#

I can't blame ya cuz I focus on fool's errands more oftent han not too

#

I bothered to figure out what a metaclass is for godssake

static bluff
#

Think about it. If I reinvent the wheel and rebuild python from scratch, think about how much better my python will be

#

In terms of both context and experience

raven ridge
#

Honestly if you want to make a language, you'd probably be better off making a few simple ones first.

acoustic crater
#

...not at all cuz microsoft just starte dpaying big bucks to ppl rdy to improve cpython

static bluff
#

But no, in seriousness, I learn best through trial by fire. Always have

acoustic crater
#

like all power to ya if you pop outta the woodwork with a superior lang but that seems unlikely for one person

static bluff
#

And in no way am I trying to replace or even come close to rivalling python

acoustic crater
#

have you ever gotten millions of people to spend thousands of hours using something you created?

#

lol yeah

raven ridge
#

Or even one like nim

static bluff
#

I know full well this project could take years, if it succeeds at all. Thats fine.

acoustic crater
#

hell just make a brainfuck interpreter

static bluff
#

On the topic of the static typing- I'm still not satisfied

acoustic crater
#

lol u rly should use a functional language

static bluff
#

I accept that my approach is wrong- you've made your point quite well

acoustic crater
#

before you get ideas about what typing is

#

imo

static bluff
#

Good advice!

raven ridge
#

Buy a book on compiler design, and let it walk you through building a language.

static bluff
#

Guhhhhh

#

You're right, you're so right

#

I havn't read a book in years

#

Any suggestions?

acoustic crater
#

damn I've got that book's existence burned in my memory and didn't even know it

static bluff
#

Rockin

#

Holy moly

#

1000+ pages XD

raven ridge
#

You're way more likely to succeed if you don't try to reinvent the entire field of compiler design from first principles πŸ˜‰

static bluff
#

So in a nutshell, if I want this to go anywhere, I've got a lot of research to do and I probably shouldn't be doing it in python

raven ridge
#

You will have a much easier time if you start by researching, and the best materials to learn from will be textbooks.

static bluff
#

I mean, it goes without saying I'm doing some research. Blog posts, articles, the occasional chapter from a textbook. Not exactly what you'd consider proper research. I usually work by refactoring some sort of working model, figuring out how each part works, and then making modifications as needed

#

But hell- the whole point of this is to become a better programmer. I suppose it's about time I got into the habit of reading a textbook.

paper echo
#

There is a lot of online formal course material on compiler and language design

#

Blog posts can be almost counterproductive if you don't have a clear learning path. Books and courses can provide such a path.

static bluff
#

Well guys, I appreciate all the help

#

I'll probably pepper you all with so many questions over the next year that you'll be sick of me XD

#

That said, I do have one little question I'd like to hear your thoughts on. I hate dunder notation. I just dislike how the double underscores look

#

If you had the chance to change it to something a little easier on the eyes, any thoughts?

#

Maybe

nondunder = someObject.init
dunder = ~someObject.init
#

I know its silly XD

paper echo
#

give every object a special __ namespace and put dunders there

deft pagoda
#

dunno, i like dunders -- when i was starting with python i knew that dunders were somehow important to the data model without knowing anything else about them

static bluff
#
someObject.dunders.init

???

#

I like it

paper echo
#
class SomeObject:
    def __.init(self):
        ...
#

im not sure it's any better

#

in fact it's probably worse

static bluff
#

Well, if we're going for broke

paper echo
#

how about this: use lua syntax instead

static bluff
#
class SomeObject:
  dunder def init( self ):
    ...
raven ridge
#

In C++ there are equally magic names, but they stand out less. Instead of __init__ it's MyClass. Instead of __del__ it's ~MyClass. Instead of __add__ it's operator+. Etc.

static bluff
#

Here's something I'm having trouble wrapping my mind around

#

Is this idea of going up by a level of abstraction. Like, if I implement my own version of a class in this language (using python, for example), how is that any different from just inheriting from a python class?

raven ridge
#

Your language exists independently of its implementation.

#

There are many implementations of Python - I know of at least 4 written in C, 1 in Java, 1 in C#, 2 in Python...

#

Your language is its semantics and behavior, not how it's implemented

paper echo
#

cpython
pypy
cinder
pyston
ironpython
jython
stackless python
micropython

any others? do shedskin and nuitka count?

rich cradle
#

Semi-related: Is it worth it to try to reimplement a language in a different language just to have fun?

brave badger
#

Brython too

raven ridge
#

I counted Cython in there as an implementation of Python in Python. It doesn't run it, but does compile it

#

CircuitPython, pycopy

sacred yew
#

rustpython

paper echo
#

i was wondering if there was a graalpython

#

apparently truffleruby is turning out to be very impressive

#

graalpython could be very interesting

paper echo
raven ridge
static bluff
rich cradle
#

Very good point

static bluff
# rich cradle Very good point

Again, I feel that reimplementing a big fat language is a fantastic way to gain both experience and context. It can't not make you better at using sed language

sacred yew
#

is lisp oop really that ugly

static bluff
paper echo
#

we really really need Common Lisp 2: Common-er Lisp

acoustic crater
#

dunders are supposed to make you uncomfortable

#

imo

#

like other janky stuff

#

only use them if you are sure they're a good idea

#

python allows most things but most of it is uncomfy for a reason

sacred yew
#

__init__ is one of the most commonly defined methods?

#

same for __str__

acoustic crater
#

yeah and how many months was it before you wrote a class?

sacred yew
#

like 3 days?

#

python isn't my first lang πŸ˜›

acoustic crater
#

lol still

#

3 days in then

sacred yew
#

but like

#

are you calling __init__, "not a good idea"?

acoustic crater
#

no

#

I'm calling it uncomfortable if you don't know what it does

#

because it is magic

static bluff
#

Dunder methods are not that scary. not scary at all

acoustic crater
#

they are just a bit uncomfy

#

and I think that's good design

static bluff
#

Each one corresponds to some top level syntax. Boom- dunders explained

#

Its not that they are some secret magic that needs to be used with care. They are backdoors into the syntax

acoustic crater
#

but they make zero sense at face value

static bluff
#

Psssssh

acoustic crater
#

so it makes sense for them to inherently cause second-guessing via syntax

static bluff
#

Sorry, that was kinda rude

sacred yew
#

constructors make zero sense?

#

you know, the thing that every class has

wheat hemlock
#

no entiendo una verga

acoustic crater
#

the only actual constructor in python is the calling of a class

#

other than u know defining classes and functions

#

technically

#

and I guess literals

deft pagoda
#

i think at least half of classes i write are only dunder methods

static bluff
#

I mean, my philosophy is to abuse the syntax to maximal effect. I use dunders like a sword. I can't count the number of times turning my object into an iterator or even just manipulating its bool value has saved my ass

acoustic crater
#

but you know what all those methods do and feel confident using them

sacred yew
#

i rarely use dunders, other than the common ones

acoustic crater
#

idk if u wanna save your ass with dunders

#

you should do rational stuff with them ahead of time

static bluff
#

I mean, I suppose it's all a matter of taste really. Use dunders, don't use dunders. Whatever works, right?

acoustic crater
#

and document it heavily

#

I think for getting into the language and the flow of things, it makes sense to ensure ppl are second-guessing dunder usage

#

just cuz they are super easy to abuse

#

I'm not syaing they shouldn't be used

#

just that it's very very easy to define them in a way that makes functionality of your objects totally opaque

#

i.e what if I defined comparison operators based on alphabetical oprering of any compared class instance's class name

#

that would use a few dunders and would seldom make any sense to do

deft pagoda
#

i mean, if writing an implementation of some abc, most of your methods will be dunder methods:

In [44]: MutableSet.__abstractmethods__
Out[44]: frozenset({'__contains__', '__iter__', '__len__', 'add', 'discard'})
acoustic crater
#

and that is the intended usage

#

especially cuz you're using an abc

#

so the second guessing is waylaid

#

and you probably have an idea of what those dunders should do intuitively if you know what an abc is

deft pagoda
#

it's not specific to abcs though --- if you're implementing an object that wants to feel like it "belongs" in python, you'll be implementing a lot of dunders probably

acoustic crater
#

yes but you should know what those dunders should do to feel that way

#

and that's how the comfy overcomes the uncomfy to make dunders great

#

myself included

deft pagoda
#

you should know what any method does before you implement it, that's not specific to dunders

acoustic crater
#

non-dunder methods have no implications or magic involved

#

they're just called explicitly

#

the only weird one there is setters and getters being overloaded but imo that's just a design improvement over other oop langs that make defining them for every damn attribute imperative

deft pagoda
#

i'm not sure what point you're trying to make, but i've decided to disagree with it in any case

acoustic crater
#

lol I'm saying dunder syntax is good

#

because it signals "this isn't a normal method"

#

to several different types of programmers

#

especially beginners and ppl who know just enough to be dangerous

raven ridge
acoustic crater
#

you can look at x.lessThan directly though

#

x < y isn't apparently __lt__

raven ridge
#

You can look at __lt__ directly as well

acoustic crater
#

but it isn't ever explicitly __lt__ or any method name

#

it's a character <

#

it's an operator

#

it should have certain behavior

raven ridge
#

Right. Dunders are the mechanism by which classes register their behavior for operators

acoustic crater
#

and even if you disagree with it it's very clear the design philosophy of python involves keeping operators somewhat pure in their functionality

#

but still permitting devleopers to do wtf they want

acoustic crater
#

and to me that involves a level of friction in absence of restriction hence dunder syntax

#

have you ever read the debates about stuff like concatenation operator vs union operator for dict?

deft pagoda
acoustic crater
#

lol I hope to be an oracle that speaks in dirty limericks someday

static bluff
#

Was hoping to find an example of three or four chained dunders together but couldn't on the fly

#

Still, nasty jazz

acoustic crater
#

that should be ugly, why are you accessing the first base like that?

#

if you know why, okay

#

do it

#

but that isn't normal python code

static bluff
#

Oh, I know, totally pointless. Just an example

raven ridge
acoustic crater
#

and that's why they're dunders

#

that is a pointless thing to do and you should second guess it

static bluff
#

Of all the things to have a phobia for

acoustic crater
#

the friction exists in the mind of skywalker

sacred yew
#

reminder that __init__, __repr__ and __str__ exist

acoustic crater
#

and for good reason

#

they are prone to the dark side,skywalkers

#

and you should be damn sure those three do what they should do!

deft pagoda
raven ridge
#

The only thing special about dunders is that they're a namespace for the special things, so that the special things can exist, and can't be confused for regular class functionality - and so that new special things can be added. If there was no namespace for these special things, adding any new special method would be a backwards incompatible change

acoustic crater
#

yeah

static bluff
#

Personally, I prefer the 'multiple buckets' approach

#

Dunders go here, other methods go there

#

No conflict

acoustic crater
#

it's just they are also uncomfy aesthetically lol

raven ridge
raven ridge
acoustic crater
#

well skywalker seems to thinkt hey are

static bluff
#

As for access (if we're talking about possibilities, not reality)


class Something():
  dunder def init( *args ):
    ...

  def init( *args ):
    ...

something = Something()

something:init( arg1, arg2, arg3 ) # dunder

something.init( arg1, arg2, arg3 ) # non-dunder
acoustic crater
#

and I was just arguing for that being a good thing

#

if ur comfy with dunders that's well n good

deft pagoda
#

: vs . is very-subtle, us sight-challenged people will die

acoustic crater
#

using colon for that is gross

#

honestly haha

raven ridge
acoustic crater
#

and the wya they are defined the same way

static bluff
#

Grosser than

__init__
raven ridge
#

Much, yeah.

woven rune
#

guys I need a legend to help me with my school hw for python

acoustic crater
#

too comfy at definition too ambiguous at retrieval

static bluff
#

Hey, I was just throwing out an idea, food for thought

acoustic crater
#

also no backwards compat with methods called init which make sense sometimes especially when metaprogramming around __init__ methods

deft pagoda
static bluff
#

Just trying to demonstrate that with one extra keyword and one extra operator, you can implement non-conflicting namespaces and still designate, visually, a seperation of usages

acoustic crater
#

but you don't need either of those things

#

and that's one more bit to add to cognitive load of parsing the lang

static bluff
#

Again, with respect, psssssssh

acoustic crater
#

instead of just being a darn method that is explicitly uncomfy

static bluff
#

I'll bet they said the same thing about decorators when they were first proposed

acoustic crater
#

why would you even have a dunder keyword when dunder means double underscore lol

static bluff
acoustic crater
#

eww

static bluff
#

or magic

acoustic crater
#

even worse

static bluff
#

Or whatever, doesn't really matter

acoustic crater
#

if you think of a better thing write a PEP and implement it and do a pull request on the cpython source haha

static bluff
#

I think dunder works fine, really. A respectful throwback to the old days, when underscores were king

deft pagoda
#

i really like dunder methods --- in other languages it's not always obvious what the "special" methods are --- even a complete beginner to python knows there's something significant about __init__ without knowing anything else about it, because why would it have such a funny name. these methods really stand out, and for good reason

acoustic crater
#

python was my first lang and dunders were mysterious and scary in a very good way

static bluff
#

O.O it is seriously spicy in here tonight!

#

I hope its not rude of me to say, I love it!

acoustic crater
#

it was immediately obvious I had to learn more to use them correctly but there wasn't more syntax to learn than how they're named

#

lol I gotta sleep but ya its been good

static bluff
#

I was the only actual programmer in my whole cohort (it's an interactive media design program at the local community college)

#

So I couldn't even talk with my classmates about this sort of stuff

acoustic crater
#

im the only python nerd at my community college rn πŸ˜›

#

but the uni im going to does python as a main thing luckily

static bluff
raven ridge
#

C reserved two namespaces for the language/implementation/standard library: things starting with an underscore followed by a capital letter, and things starting with two underscores. Python reserved one, things starting and ending with two underscores.

acoustic crater
#

javascript shoved everything into the same namespace then added another one then added symbols haha

static bluff
#

Ohhhhhh Javascript

#

That loveable dumpster fire ❀️

raven ridge
#

Guido's choice of namespacing for special methods in Python was presumably heavily influenced by how C reserves names for the implementation, come to think of it

static bluff
#

I mean, I'm probably going to go the explicit route, have a namespace for each of public, private, and dunder

#

I personally don't like the idea of people being able to touch the so called private attributes of my classes in Python. If they want to screw with the functionality and such, they can subclass

#

Godly I've got one more for you

#

I'm self taught and my coding style is proudly unusual. But that doesn't mean I don't want to know the proper way of doing things

#

If I have a big and cumbersome class, like a lexer, I usually like to break it into many smaller classes each in their own modules. For example

  • Lexer
    • init.py
    • Lexer.py
    • LexerAbstract.py (the lexer metaclass)
    • LexerConstructor (initialization and properties)
    • LexerWalker (iterator protocols and the main body the lexing process)
      ... (other classes devoted to specific portions of the lexing process)
#

Bad form?

raven ridge
#

Not how I'd do it, personally. Needlessly spreading functionality across many classes is Java-esque and enterprise-y in all the worst ways.

deft pagoda
#

you can also create circular dependencies if you break up your class too much into separate files

#

which i've done to myself enough times

raven ridge
#

You'd have to remove a lot of language features before your privates would actually be relatively private.

deft pagoda
#

anyone ever seen changing types in the wild?:

In [51]: class A:
    ...:     def say(self):
    ...:         print('hi')
    ...: 
    ...: class B:
    ...:     def say(self):
    ...:         print('hello')
    ...: 
    ...: b = B()
    ...: b.__class__ = A
    ...: b.say()
hi

I didn't know this was a mutable attribute

#

oh, that gives me an idea

deft pagoda
#

being able to swap multiple methods at a time of some object i can guess is useful for testing, but i think i can find other neat uses as well

static bluff
#

You could abuse the crap out of this XD

#

Also, regarding your concept of ownership, I've always found it odd that a class doesn't exist during it's definition. I do understand that, as it stands, the class collects everything in it's definition space and then initializes itself, but it seems odd they didn't simply inject the uninitialized class instance into the definition space. By the nature of the fact that the parser knows its in a definition space, a reference to the class or at least its context exists somewhere

#

As for access to private variables, I guess you could have a designation on functions within the class as being either 'native' or 'foreign'. Anything that was present in the class at the time it was instantiated is native, otherwise, foreign

raven ridge
flat gazelle
#

So, the way classes work is that it calls a metaclass with it's environment, bases and such and then the metaclass gets called and creates the complete class. A half initialised class never exists, it's just a dict and a tuple, which once full are made into the type object

flat gazelle
#

But inside methods, you can use __class__ to get the class the method is in. It's a parser trick iirc

raven ridge
static bluff
#

It's all about context though, right? Knowing whether or not the accessing agent is or is not an instance of the class

raven ridge
#

Right. In all of those cases, it's not.

static bluff
#

And the accessing agent could be a module (or module-like object, in the case of an eval or exec) or a function/method

#

(Just asking for the sake of understanding)

raven ridge
#

In fact, it would break getattr and setattr entirely. They currently only need to know what object they're accessing, and what attribute from it. You'd need a different contact where you pass them an extra piece of information, who is requesting to get or set this attribute, so that they can choose whether or not to allow it.

static bluff
#

But deep deep down in the interpreter, that data is available

raven ridge
#

Not today, it's not

#

There's no concept of ownership over methods, like I said.

static bluff
#

At the moment the data is requested, the scope of the operation is visible. Could the identity of the scope's owner not be passed?

#

Ahh, I see

raven ridge
#

You don't just want to know the owner, you want to know whether the owner is privileged to access the private

static bluff
#

As we're talking, I'm getting a clearly picture of what it is I actually want

flat gazelle
#

Very few languages have access modifiers that are actually always enforced. Java, C#, C++ all give ways for other classes to access private attributes. The only thing python does differently is that bypassing it is much cleaner.

raven ridge
#

And, things like pickle and copy.deepcopy and debuggers access attributes of arbitrary objects as part of doing their normal job. Making it so that there are attributes they can't access will cripple them.

static bluff
#

I want a language that support privacy and type enforcement, top-level syntax matched to dunder methods, iterator protocols, indentation based code blocks, python style importation, multiple inheritance and metaclassing

#

And a a few other python bits and boops, as well as an undefined object and readonly attributes, and javascript's ability to call simple objects into existence on the fly (as a bonus. Not super important)

#

And, for love of god, arrow notation

flat gazelle
#

The prefix _ convention is about as strong as access modifiers get in general purpose languages exactly to make metaprogramming easier

static bluff
#

And if that's what I want, I should use python as a roadmap but not a scaffolding- and keep my mind open to how other languages are implemented

#

Plus, do a butt-tonne of proper research

flat gazelle
#

If you want to look at statically typed python, there is rpython

static bluff
#

Ohhhhh holy moly

#

So much to learn

lament sinew
#

What's arrow notation?

flat gazelle
#

(lambda, arguments)=> lambda body

#

No idea how you would make it useful in python, since blocks as expressions don't look all that nice. Just look at nim for a language that has all statements as expressions and the same indentation based syntax as python

static bluff
#

Well first off, you wanna talk ugly; lambdas are ugly

#

Also your arrow notation is a little off. Pythonically it'd be

( *args, **kwargs ) => { body }
#

Arrow notation is hugely useful for callbacks and they make life a lot easier in event driven situations. Basically anything that runs in a loop for the lifespan of the program

#

Additionally, as with any function defined within another function, they can see the scope of the function they're embedded in

lament sinew
#

I once wrote this

#

As a kinda joke

#

Won't run on the bot because of frame shananigans, but works most of the time.

static bluff
#

Consider:


def someMethodThatTakesACallback( callback, *args ):
  return callback( *args )

def callback( a, b, c ):
  return a + b + c

result = someMethodThatTakesACallback( callback, ( 1, 2, 3 ) )

versus


def someMethodThatTakesACallback( callback, *args ):
  return callback( *args )

d = 4 # added

result = someMethodThatTakesACallback( ( a, b, c ) => {
  return a + b + c + d # added d
}, 1, 2, 3 )
#

In my opinion, cleaner. Its also well established in a lot of other languages, so it's not exactly a ground breaking concept

#

Sorry, I'm gonna make one more modification- one sec

#

The anonymous arrow function can see the scope it was created in, and therefore, can access 'd' even though its being kicked around like soccer ball

lament sinew
#

I find block lambdas kinda overrated in a language like python, with first class functions.

flat gazelle
#
@takes_a_callback
def result():
   Code
```why not use a decorator
#

That's what most event driven libraries use

#

You can do it inside function bodies if you need closures

static bluff
#

Could I see an expanded example? I'm having a hard time visualizing what it would look like in the wild

#

Here, one sec

#

element.addEventListener( 'load', ( event ) => {
  print( event.type )
} )

Say for a moment we were in a python based web browser, where the elements in the dom were python objects yada yada yada. Both the element itself and the callback you're providing are being used on a variable level, outside any type of class definition

#

How would decorators be used?

flat gazelle
#
def main_of_sorts():
    element = ...
    
    @element.eventListener
    def load(event):
        print(event.type)
#or
    @element.eventListener('load')
    def my_event(event):
        print(event.type)
static bluff
#

I will admit, that is rather sexy

#

I wouldn't exactly say better, but still rather elegant

flat gazelle
#

I do find it nicer than the JS approach, though both have the same issue callbacks have which is you can easily get into callback hell and just end up with massively nested functions/arrows.

paper echo
deft pagoda
#

i've done similar decorators for binding methods to attributes, e.g,:

@bind('width', 'height')
def _resize(self):
    ...
static bluff
#

πŸ™‚ I don't see any reason the two couldn't coexist happily. There will be cases where both will work interchangeably, and cases where for whatever reason one trumps the other

flat gazelle
#

there is also the kivy approach which uses on_thing named methods and just calls them dynamically with getattr

deft pagoda
#

yes, i like that too, but it's pretty implicit

#

you can bind things manually in kivy though

#

my_property.bind(...)

flat gazelle
#

I would rather not have 2 very similar, yet slightly different tools. 4 string formatting options speak enough about why that is a bad idea.

static bluff
#

I think that's a bit of a different case

#

fstrings are unambiguously the best, with .format() as a distant second. That there are 4 is simply a matter of legacy baggage being carried around

#

(In my opinion)

deft pagoda
#

format has some nice use-cases

static bluff
#

Like passing **someDict

deft pagoda
#

yes

flat gazelle
#

there are cases where you want % formatting for strings that contain {}, and string.Template if you want to pass format from users

static bluff
#

Which, I think, feeds in to my original point

#

Just because the syntax is there doesn't mean you have to use it- but when the time is right you're glad it's there

#

And, lets be honest, on the decoratee's end decorators are great, but writing them can be a pain

#

At least for me and I'm sure for a lot of novice programmers. Arrow notation might very well be easier to wrap your mind around- if perhaps a shade less powerful

#

As I think about it, actually, they might operate similarly in some contexts but they are completely different animals in others. Arrow notation is a no frills multiline lambda, while decorators are wrappers around functions. Their nature's are quite different (not meaning to spam, just thought it was a point worth making)

flat gazelle
#

the issue is you have 4 extremely similar features which have slightly different usecases and can have surprises (user defined format strings for .format are often unsafe).

#

the only case in which you need a multiline lambda is as a callback

#

and decorators have handled that well enough in most cases

static bluff
#

Soooooooo not true

#

Multiline lambdas are as useful as you make them. The only reason python doesn't have them is that sorting out the syntax is a bit of a pain (it conflicts with a few already well established aspects)

#

But as a general rule, they are a perfectly useful tool in all sorts of contexts. Filters and maps for example

#

hehe, with a bit of creativity, I'll bet you could even rig up some sort of 'refresh' method attached to them, such that they replace their original outer scope with their current outer scope, wherever they may be, and you could have traveling methods that could see outside themselves (don't read too much into this, its just a thought)

#
anon = ( a, b, c ) => { return a + d, b + e, c + f }

d, e, f = 1, 2, 3

print( anon( 1, 2, 3 ) )

>>> ( 2, 4, 6 )

def someFunction( anon, a, b, c ):
  
  d, e, f = 10, 20, 30

  return anon.refresh( locals() )( a, b, c )

print( someFunction( anon, 1, 2, 3 ) )

>>> ( 11, 22, 33 )
#
result = map( ( value ) => {
  if value is True: return 'The value is True'
  elif value is False: return 'The value is False'
  else: return 'The value is None'
}, [ True, False, None ] )

Just posting this for others who might want to check out our discussion in the morning. I'm curious what it is you're typing

#

Well it's just an example πŸ˜›

flat gazelle
#
    this.sessions = this.sessions.filter(session => {
        try {
          session.webSocket.send(message);
          return true;
        } catch (err) {
          session.quit = true;
          return false;
        }
    });
```there is this example of a filter multiline function, and while it is pretty, is it really a meaningful improvement over the more conventional.
```py
for i, session in enumerate(sessions):
    try:
        session.web_socket.send(msg)
    except ClosedError:
        session.quit=True
        sessions[i] = None
sessions = [session for session in sessions if session is not None]

Like yes, there are cases where they work out nicely, but is there a case where they are enough of an improvement to be worth it. You can always come up with a solution that is adequate. Python doesn't really have the facilities to support things like java or JS fluent list manipulation. map is already often near illegible and imagine having multiple of them nested with filters inbetween to emulate .map .map .filter. Is .map .map .filter a nicer solution? Probably. But the thing stopping python is not multiline lambdas, it is being inherintly designed around statements in tradition of a specific algol version which I didn't memorize. While you could pivot the entire language, it is probably easier to just use a whole different language. Nim for example has all the features you need for this and some extra.

static bluff
#

With respect, all I ever hear from you guys is 'no, no, no'

#

Which is good, it keeps me focused

#

It's a bit depressing though πŸ˜› Here I am trying to apply creativity and intelligence to push the envelope- and challenge preconceived notions

#

And a bit of optimism would, I dunno, would be nice as I embark on this journey

lament sinew
#

What you call pushing the envelope, I call bloat.

flat gazelle
#

keep in mind that python is meant to be simple, so any feature must really improve on things to be considered. Despite what functional programmers try to tell you, heavily expression based languages are harder to explain and learn, so keeping the imperative spirit is pretty desirable. If you look at scratch, logo, basic etc. they are all even more imperative than python

lament sinew
flat gazelle
#

it is very easy to learn, which is really what you need

static bluff
#

It's easy to learn but also endlessly deep. Calm but endless waters

flat gazelle
#

every language is endlessly deep

static bluff
#

You'll never have too much trouble with any particular aspect of Python, but you'll never be able to find solid ground either

flat gazelle
#

there is always deeper to dig, more libraries and conventions to learn.

#

be it JS, java, C#, VB, scheme, Elm, haskell, C

#

you can't have a finite language, computing is far too vast for that

lament sinew
# flat gazelle it is very easy to learn, which is really what you need

I thought so long ago, but now I'm convinced it's not. Python gives the air of being simple, but the realities of immutable vs mutable data structures, passing by value vs reference, bad error messages, abuse of try/except statements, etc. These hit first time learners early and it hits them hard.

#

That without mentioning the non-language stuff, like managing dependencies and versions.

static bluff
#

You know what I think my biggest take away tonights discourse has been? Everyone has their own way. I can build or use whatever I want to build or use, and so can everyone else.

#

πŸ˜› That said, I'm gonna get cracking! I think I still have a few hours left in me

flat gazelle
#

error messages are improving, immutable vs mutable is a thing in every language, you always pass by reference in python. Like yes, it isn't the absolutely perfect beginner language, but nothing is really better

lament sinew
#

immutable vs mutable is a thing in every language
But in a dynamic language like python they're particularly confusing to beginners.

flat gazelle
#

any type system that is expressive enough to be useful is a massive learning curve. What is the type of list.append? What about list.__add__, ...

#

if you look at simplified languages like scratch, basic, logo, they are all dynamically typed, since that is one less thing to learn

#

I am not claiming there are no pain points to python, but I don't see a better option

#

I started with pascal, and it took me quite a while to even get to something. Python learners here are much faster with starting out.

#

and pascal was also meant to be simple to learn

lament sinew
#

Pascal is a good language.

flat gazelle
#

char c = 'a'; sqrt(c); this compiles, but that is beside the point.

How many people would be confused by the type even for something as simple as lists. How many people learning C in uni spend hours upon hours struggling with arrays.
One of the most common questions of java learners is "why is my program with lists not compiling"
Type systems look nice and simple until you need anything but scalar types. Even in pascal, arrays are really annoying to get right.

static bluff
#

I'm with Lak on this one- Python is by far the least worst option when it comes to learning

#

It's clean, sensical, and opens itself up at a reasonable pace

flat gazelle
#

is it possible to teach type systems well? Yes
Do people have access to such teachers? No

lament sinew
#

I'm not defending C as a learners language that was just an example of how types help first time learners (even if the example was wrong), while dynamic typing creates confusion.

static bluff
#

I just finished my first year in college and we had a coding course. I'd been coding almost a decade at this point so it was a total snooze, but

#

Watching those poor bastards (my classmates) struggling with the anatomy of even a basic C# assignment was horrid, and there is good reason for the struggle. Type declaration and conversions, hideous for loops, privacy, not to mention it throws you directly into classes without any preamble

#

Python just has less stuff, on its surface. Its all still there but you don't have to worry about it until you're ready

peak spoke
#

is mutability/immutability that big of an issue? It should be clear after a proper (short) explanation

static bluff
#

I firmly believe that if you're going to teach a C-type language to absolute beginners, you start with two weeks of python

#

Fewer tears that way

lament sinew
#

But suffering through a code that does not compile is easier than suffering through a code that runs and crashes. Most first time learners dont' have the know how to debug runtime errors at all.

peak spoke
#

When learning the scripts will be short enough that runtime and compile errors are almost synonymous as they will pop up immediately when running

static bluff
#

You've got to feel it

lament sinew
#

I don't think this analogy applies at all.

#

That would be the equivalent of teaching someone to code with pseudocode.

static bluff
#

Says the guy who most likely plays Python like first-chair plays the violin

#

Programming absolutely is like playing an instrument, and is absolutely is something you have to feel. No one can tell you how to do it- you and the language have to do a dance together

#

Until eventually you're reading each other without thinking

lament sinew
#

Getting compile errors is practice, not theory.

static bluff
#

Oh oh oh

#

One thing I really wish Python had

flat gazelle
#

one bug a statically typed language would fix is reusing a variable name, that is true.
But having None instead of a string means that you at some point misunderstood how your code flows. In a statically typed language, you would likely have null or "" in it's place, and may not even get an error at all. It may make you realize that something is wrong, but errors in logic are often padded over with whatever the hell makes the compiler shut up.
Beginners often feel that the compiler is wrong. you can see it in C and C++ best where people will just put & and * until the compiler stops yelling at them about type errors, even if they invoke UB. Especially if their line of thinking was correct, but failed to transfer it to code.

static bluff
#

Object destructuring (or, named-destructuring might be a better term)

#

You can do it for iterables, mostly, but named would be great. It could work on dicts and by extension, objects

flat gazelle
#

boy do I have news for you

>>> from dataclasses import dataclass
>>> @dataclass
... class A:
...    a: int
...    b: int

>>> match A(a=34, b=5):
...     case A(a=a, b=b):
...         print(a, b)
34 5
>>> print(a, b)
34 5
```python 3.10 does have just that
static bluff
#

YAYYYYYYYYYY

flat gazelle
#

it is one of the more controversial features, since patma is inherintly complex and the syntax is very mediocre

static bluff
#

patma?

flat gazelle
#

pattern matching

static bluff
#

Ahh, thought so πŸ˜›

flat gazelle
#

but it does solve a lot of problems that came up in python libraries

#

if you look at the public API of quite a few libraries, you will see like 8 lines of normalizing arguments followed by 3 lines of actual logic

static bluff
#

Yupppppppppp

#

I mean, I have no idea what you're talking about, except that arguments can be a pain the ass

flat gazelle
#

there is this little if in almost single turtle function

def fun(x, y=None):
        if y is not None:
            pos = Vec2D(x, y)
        if isinstance(x, Vec2D):
            pos = x
        elif isinstance(x, tuple):
            pos = Vec2D(*x)
#

even after that, it will be a few months before libraries port and a few more until the mainstream ports

snow kettle
flat gazelle
#

I do not

snow kettle
#

i've seen a module that provides something similar to it, but isn't fully compliant with the pattern matching pep yet

#

run-time type validation be a lot cleaner to write with it

static bluff
#

On that note, I've been thinking long and hard about automated runtime argument validation

#

I've heard the arguments against and so I'm not holding my breath, but it occurs to be that one could apply regex to the problem

#

I'm a bit too tired to go into it right now, now that I think about it, but as I continue to ponder over the next few weeks, I'll show you guys what I'm thinking

gleaming rover
#

especially when you don't even start learning Python for Python, but Python for DS/ML

#

πŸ₯΄

flat gazelle
#

yeah, not trusting the compiler is pretty universal

gleaming rover
#

and the candidate talked about how she learnt that a lot of programming is identifying the mismatch between what code actually does and your mental model of what it does

static bluff
#

I've had people explain to me that validating simple types like strings or bools is quite simple, but complex or compound or component typing is difficult.

def func( 
    argument : Callable[ [Iterator, Callable], Generator[int, float, str] ] 
  ):
  ...

Not so simple.

Checking this manually would be a pain, and there might not be a way to cover all the edge cases. But if you instead converted objects into patterns, using letters or keywords perhaps to indicate their various characteristics, you might be able to apply regex to ensure that needs are met. I havn't formalized any of this, so it's all just spitballing at this point but

# A class's 'encoding' would consist of Classname[:methods[:arguments[:returntypes]]]
# arguments/returntypes only applies if class is callable

class Object(  ):
  # this class would encode to Object:next|call:[.+:next, .+:call]|[generator:int|float|str]

  def __next__():
    ...
  
  def __call__( a : Iterator, b : Callable ) -> Generator[int, float, str]:
    ...

If you can encode both the functions signature, and all of the arguments provided to it, you could do a regex check to make sure they match. Steps could be taken to allow multiple possible types, specific numbers of a given type, etc.

Just an idea I had πŸ˜›

gleaming rover
static bluff
#

Again with the naysaying

#

Use your imaginations, people!

gleaming rover
#

reading about Monad Transformers

#

that's where my imagination goes

pulsar pier
#

you mean attention?

gleaming rover
#

I like the limits of static typing more than those of dynamic typing tbh

gleaming rover
#

the monad transformer is a construct in functional programming

#

that allows you to compose monads

pulsar pier
#

oh y see,
you mean the functional programming right/

#

?

gleaming rover
static bluff
#

Night all

feral cedar
sacred yew
#

I've seen enough C/C++ questions on SO to know that when there's an error, its much much harder to debug than any dynamically typed lang

#

compare an error traceback with a segfault

flat gazelle
#

yeah, C/Cpp runtime error reporting is atrocious, java does pretty well though

#

there is a python PEP to allow reporting not just the line the error is on, but also which column it happened on

#

so you would see pretty exactly what went wrong even in long lines

#

if it can be implemented well, that is probably a PEP that will be accepted

feral cedar
#

!pep 657

fallen slateBOT
#
**PEP 657 - Include Fine Grained Error Locations in Tracebacks**
Status

Draft

Python-Version

3.11

Created

08-May-2021

Type

Standards Track

sacred yew
feral cedar
#

<@&831776746206265384>

granite heath
#

!ban 810292495199502375 Take your trolling elsewhere.

fallen slateBOT
#

:incoming_envelope: :ok_hand: applied ban to @stuck beacon permanently.

grave jolt
#

@paper echo re: #weekly-topic-discussion Some people define a framework as a library with inverted control, i.e. instead of you calling its functions, it's calling the functions you provide in a specific way.

#

which sort of makes sense

static bluff
#

It'll automatically build a heuristic that will cover all relevant possibilities quickly. Also regex itself is fast and 'trustworthy', which is to say, not experimental

steel solstice
#

Does anyone know the rationale behind types.Union supporting isinstance and typing.Union not and why you shouldn't just use a tuple?

acoustic crater
#

you can just use | in 3.10

acoustic crater
#

instead of a Union

grave jolt
#
  1. you can't just use a tuple because type checkers don't treat tuples as union types
#

I mean, you can, nobody is stopping you

#

but it won't work with the tools

#

Why it is that way? I don't know. Maybe ask in typing-sig

steel solstice
#

Might do

#

Thanks

paper echo
#

for x : A, y : B, the tuple (x, y) is (isomorphic to) an element of the cartesian product A x B

#

basically, the notation is already taken by another well-established concept

neon tartan
static bluff
#

I really want to thank you guys

#

Last night really got my engine revved up. You've all given me a lot of perspective, but also emboldened my sense that I'm going about doing what I'm doing for me, and I can program it however I want. I needed the confidence boost

#

πŸ™‚

grave jolt
paper echo
grave jolt
#

yeah

paper echo
#

maybe it leads to parsing ambiguity? it'd be nice for sure.

grave jolt
#

what ambiguity? it's totally valid syntax anyway

#

maybe there's some requirements for type hint objects to have some special fields?

paper echo
#

yeah idk, it's a good question. maybe they just didn't think of it, or didn't think tuples were special enough

#

oh, you know what? i bet | is implemented with __or__

grave jolt
#

ah, right

#

yes

#

but that couldn't've been the original reasoning

paper echo
#

"we can implement it with existing techniques instead of adding a special parsing case" seems on-brand for python

#

in other news:

def f(x):
    x += [7,8,9]
    return x

data1 = [1,2,3]
data2 = f(data1)

assert data1 == [1,2,3], "This sucks :("
grave jolt
#

you mean data2 = f(data1)?

paper echo
#

yes, fixed

grave jolt
#

!e

def f(x):
    x += [7,8,9]
    return x

data1 = [1,2,3]
data2 = f(data1)

assert data1 == [1,2,3], "This sucks :("
fallen slateBOT
#

@grave jolt :x: Your eval job has completed with return code 1.

001 | Traceback (most recent call last):
002 |   File "<string>", line 8, in <module>
003 | AssertionError: This sucks :(
grave jolt
#

well... makes sense?

#

ah, you mean the fact that += is extend?

paper echo
#

yes, i hate it

grave jolt
#

yeah, it's a lemon_pensive moment

unkempt rock
paper echo
#

naively, i think most people would expect "shallow copy on write" semantics here

unkempt rock
#

so you would rather have all augmented arguments not modify self?

#

isnt that exactly what the normal operators are for?

#
def f(x):
    return x + [7,8,9]
grave jolt
static bluff
#

I don't think it's strange at all

unkempt rock
#

exactly, so you wish for augmented arguments to always be name reassignments

grave jolt
#

Like, imagine explaining it to beginners.
"x += y just means x = x + y, except it doesn't"

static bluff
#

When you think about what's going on under the hood, it's a different process

unkempt rock
#

thats a good point actually, there's no point in having augmented operators be special for mutables

static bluff
#

Or maybe I'm wrong. But it seems perfectly sensical to me

grave jolt
#

I don't care how it's implemented. In most cases, x += y means x = x + y

fallen slateBOT
#

@warm kernel :white_check_mark: Your eval job has completed with return code 0.

001 | (1, 2, [3])
002 | 'tuple' object does not support item assignment
003 | (1, 2, [3, 1])
static bluff
#

You don't care- but the machine does

unkempt rock
#

methods already exist for mutating why not maintain consistency in +=

#

thats the point fix is trying to make

grave jolt
# static bluff You don't care- but the machine does

The semantics of the language aren't concerned with the machine, not sure what you mean.
On lists, x += y mutates the list (same as x.extend(y)), which goes against the expectations of x += y being the same as x = x + y for every other object in Python, and for many other languages (where those are literally synonyms).

static bluff
#

Mutate, as opposed to the creation of a new object?

static bluff
#

I suppose that is a bit odd. Do think it's for performance gains?

grave jolt
static bluff
#

Goodness gracious

#

More syntactical sugar is better

grave jolt
#

turns out += actually isn't syntactic sugar for addition πŸ™‚

flat gazelle
#

at the same time, how many beginners would write

for a in b:
    c += [a, -a]
```and get O(n^2) loops. CPython even takes care of it for strings in cases where it doesn't matter
static bluff
#

And if they made the choice to go with extension over the creation of a new list, probably for performance reasons, then I'm going to have to trust them

grave jolt
flat gazelle
#

it is just odd because it is inconsistent with tuples for example

#

yeah, that's a fair point

unkempt rock
#

oh the mutable vs immutable thing yeah

flat gazelle
#
a = b = []
for i in range(4):
    a += [i]
#vs
a = b = ()
for i in range(4):
    a += (i,)
static bluff
#

I'ma ask a totally off topic question over in off topic- but I'm mentioning it because you guys are all smart as ballz

grave jolt
#

even taking into account the different possible meanings of 'balls', balls aren't particularly intelligent

static bluff
#

Why are deer balls the cheapest type of meat?

#

Well go on, ask me why

paper echo
static bluff
#

Each one is under a buck

paper echo
static bluff
halcyon trail
#

how does x = x + y behave differently than x += y for lists

#

because of the mutation?

feral cedar
#

it doesn't create a new list

#

yeah

halcyon trail
#

it's a lot more efficient that way

feral cedar
#

and unintuitive

halcyon trail
#

not really, not to me anyhow. In most languages I can think of that do something like += for a mutable type, you have mutation

feral cedar
#

really? examples?

halcyon trail
#

C++

#

Kotlin

#

it's really the other behavior (creating a new object) that's not intuitive, if you think about it, because you would expect += to correspond to a dunder method

#

and you can't reseat a reference in a function, in python

#

so that means that the kind of += you want would need to involve some kind of magic

#

And for integers (which are immutable) it does

#

Whereas a mutating += can actually be implemented directly as a dunder method

flat gazelle
#

a += b is

res = a.__iadd__(b)
a = res
halcyon trail
#

ah, so it does do a bit of magic to support reseating

#

good to know

flat gazelle
#

that's how the operator is defined

#

it wouldn't work otherwise

halcyon trail
#

well, it would work fine for list otherwise

#

but yes, not for other things

flat gazelle
#

it wouldn't work fine for list otherwise, since it would elude setattr and setitem dunders

halcyon trail
#

sorry, I don't follow

raven ridge
#

It would work for extending a list, but not for setting an item in a list. lst1 += lst2 would work, but not lst1[0] += lst2[0]

halcyon trail
#

right

#

I meant the former of course

magic python
#

is having a class within the init of another class an antipattern? It works, and part of me thinks its reasonable as it's only used within the scope of the init, but I'm not sure

#

eg:

class C:
    def __init__(self):
        class D:
            def __init__(self,a, b):
                self.a = 1
                self.b = b
        self.x = D(a = 2, b = 3)

usage:

c = C()
c.x.a
c.x.b

etc.

flat gazelle
#

why not define the class outside the init?

magic python
#

didn't work outside the init

#

πŸ˜…

#

you mean outside the class entirely @flat gazelle ?

flat gazelle
#

yeah

magic python
#

idk, bc it's only used in the class within the init πŸ€”

flat gazelle
#

that's fine

magic python
#

like sometimes we'll leave a function within a function if it's only used there ? why not with a class like this

flat gazelle
#

if you just need some attributes, you could also just go with a SimpleNamespace

magic python
#

never heard of that - i just wanted to have class.data.raw, class.data.interim etc in metadata class

flat gazelle
#

oh yeah, simplenamespace is probably the way to go then (or a dataclass

magic python
#

I'm trying to use dataclass, but i still have __init__ πŸ€” maybe i'm using them wrong

flat gazelle
#

if you need an init in a dataclass, it either shouldn't be a data class, or use __post_init__

#

but honestly, I would just put the class outside

magic python
#

no idea what i'm doing really so it could be anything

flat gazelle
#

it's fine to have some extra classes hanging around

magic python
#

fair enough

flat gazelle
#

you can prefix it with _ if you don't want it as part of the API

magic python
#

api is a strong word for this thing : ' )

#

i'm just trying to get all the metadata into a single location, atm the class doesn't have any input to the __init__, other than self, but when creating it with x = Metadata() it didn't really work as well, and using __init__ made it work πŸ€·β€β™‚οΈ so i used that

grave jolt
dawn arch
#

whatever params are defined in __init__ should be passed to Metadata()

magic python
#

@dawn arch a lot of them are just written in - it's stuff like the project root / data path / storage location etc

dawn arch
#

i mean, if you have __init__(self, color, number) you need to call Metadata('red', 8)

grave jolt
magic python
dawn arch
#

i wouldn't define a class inside the init of another class, definitely smells to me

sacred tinsel
magic python
#

need to dig in a bit more

sacred tinsel
#

I'd consider it unexpected, unless there's a reason for the classes to be built dynamically e.g. depending on some param to the init

#

For example, if you annotate the attr:

>>> class A:
...   x: B
#

which B? each isntance has its own
.. this is hypothetical because you can't annotate it for B isn't visible πŸ˜”

magic python
#

I'm doing so much wrong i'm sure πŸ˜…
i'm putting the metadata into a class, then am frequently doing meta = utils.MetaData() and using that in the module πŸ€”

#

i'm trying to make all the stages independent though , and am running them as a pipeline, couldn't really find much on that kinda thing

halcyon trail
#

@grave jolt in C, that's because = itself is also mutation

#

you have the equivalence the wrong way around

#

= in C and C++ is not the same as = in languages like python, Java, etc

#

+= and = in C are equivalent because they both mutate

grave jolt
#

Well, yes, C has completely different semantics.
My point was that in C, x = x + y is the same as x += y

halcyon trail
#

But C doesn't have operator overloading so it's not terribly interesting to discuss. the same with Java.

#

Yes... because they both mutate πŸ™‚

grave jolt
#

not necessarily

halcyon trail
#

not sure what you mean by that

#

operator= in C is a mutating operation

#

but anyhow, again, in languages without operator overloading having these discussions is somewhat pointless

grave jolt
#

If it's a stack variable, e.g. a number, there isn't really a difference between mutation and reassignment

grave jolt
halcyon trail
#

well, that's the thing, when you are only discussing primitive types it lessens these distinctions, and makes things seem more similar than they are

#

It doesn't "redefine"

#

it defines

#

+= for strings in C++, for example, appends/extends

#

similar for /= and +=

acoustic crater
#

I was against augmented assignment mutating till I saw this convo and looked up the PEP

#

their rationale is pretty good

grave jolt
#

Hm, okay. += mutates strings.

acoustic crater
#

nothing mutates strings

grave jolt
#

in C++

halcyon trail
#

we're talking about C++ momentarily

dawn arch
#

in ruby you can mutate strings

acoustic crater
#

oh

halcyon trail
#

also Path objects

#

the signature that operator= is supposed to have, and given ownership semantics in C++

#

+= can never really do anything except mutate

#

so it's not really allowed to return a fresh copy at all

dawn arch
#

isn't it just syntactic sugar?

grave jolt
#

Okay, in C++ some collections implement += as mutating. It also has quite different semantics from Python.

My point still stands that in most languages (whether they have operator overloading or not) where += exists, += is just syntactic sugar for +.

halcyon trail
#

My point stands that in most languages where += on mutable things exists, it mutates them πŸ™‚

grave jolt
#

what are those languages? C, C++ and Python?

halcyon trail
#

And Kotlin

#

The only counter-example I've seen is C#, if C# actually has + for mutable collections

#

which I'm not sure it does

#

So, I've basically seen zero examples where x += y, if x is of a mutable type, would not mutate x

#

Kotlin btw actually lets you have your cake and eat it too, although some people probably don't like the ambiguity
If x is a val (non reseatable reference) to a mutable type, += mutates
If x is a var (reseatable reference) to a non-mutable type, += creates a copy and reseats the reference

grave jolt
#

ok that's cursed πŸ‘€

halcyon trail
#

It's actually pretty fantastic

#

it adds quite a bit of convenient to read-only interfaces

#

But okay, there's still no example where x += y, where x can be mutated, does not mutate x. Python's list is mutable, so that's why it's intuitive to me that x += y mutates it

#

Realistically I was coming primarily from C++, and in C++ that's the only way x += y can work, so I'm sure that's the main reason it was intuitive to me