#esoteric-python
1 messages · Page 30 of 1
i don't want to be like all these cool big languages out here ;p
so you want to do it in like a bizarre way, or still practical but just unique?
Lambdas in python do that too (but they cannot contain statements at all)
both
im open for ideas
Pascal and delphi have special variable result
To return something from function, you should assign to result and then exit from function
function add(a: integer; b: integer): integer
begin
result := a + b;
exit; // not needed in this particular case; does the same as plain return in other languages
end;
``` probably i messed syntax somewhere, but you get the idea
Yeah, so result is a key word and probably can't be used as a variable name?
Idk, probably it is special-cased
Like this in cpp
Other idea: dont return anything from functions
All functions have side effect: they assign their result to global variable result
add(1, 2)
print(result) # 3
a language called DM has a special variable inside functions called ., which is returned by default if the function ends without an explicit return
Finally, .= operator
^ i wanted to suggest that too
something like
fn max(a,b)
{
if(a > b)
{
.= a;
}
else
{
.= b;
}
}
and its quick to type
oh, also, a bare return without a value also returns .
I like and dont like that at the same time
big yikes from me
so if you don't assign to . it doesn't return anything? actually that kinda works
without . set it returns null. or another way to look at it is that . is set to null by default.
on the other hand, x .= y as a shorthand for x = getattr(x, y) doesn't sound bad at all
it'll remove most needs of the ?. operator
DM is fairly weird... it started out as a scripting language for making MUDs back in the 90s, then grew quite a bit from there. it's got a few idiosyncrasies.
Nim has a similar thing, it's just called result
Could probably make that work in python
Btw, what functionalities would u like to see in python?
And what would you change?
I would for sure add i++, i--
Is this the best channel to ask about the nitty gritty of "faster cpython" optimizations in 3.11? Please let me know if there is a better channel for this question.
I'm curious how the "Load attribute" optimization works, and when it works.
Similar to loading global variables. The attribute’s index inside the class/object’s namespace is cached. In most cases, attribute loading will require zero namespace lookups.
https://docs.python.org/3/whatsnew/3.11.html#whatsnew311-faster-cpython
If I have a class Foo: pass and I make two instances a = Foo() ; a.bar = 1 ; a.foo = 1 ; b = Foo() ; b.foo = 1 ; b.biff = 1
Does this mean a_or_b.foo is optimized? And how? Because a and b have different dictionary layouts.
Maybe #internals-and-peps? Not sure though
thanks, I didn't see that one, sounds good
Okay, how about for loops
how would you guys do it?
I was thinking about doing it in rust style
but when I saw this abomination
for i in 0..array.len()
i decided not to ;p
Im not sure. Here is my guess:
No, that would not be optimized. I think this optimization work only if instances has shared keys in their __dict__. This happend only if you are assigning all attributes in __init__ and not adding new attributes outside of __init__
I'm guessing that the dictionaries have some sort of a field describing their "shape ID" or something? So if each dictionary has identical shape -- same keys stored in the same layout -- then they share a "shape ID" and the optimization checks this shape ID to see if the cached offset is valid?
My worry is that the optimization actually never works for different objects, only the same object.
So foo.some_method is optimized because it's doing a lookup on the class itself (singleton)
And math.PI is optimized because math is similarly a singleton
But a_or_b.foo is not optimized because the object is different each time, not a singleton
I see how split-table dictionaries work now, and understand what you meant about assigning all attributes in __init__
https://peps.python.org/pep-0412/#split-table-dictionaries
I guess the optimization also fails if the code is generic and accepts subclasses of Foo, because each subclass that adds its own fields will have different __dict__ keys
and then u need to add .step_by(2) on top of that if u want to step by 2
that sucks
for var in 1 to 10:
what about step?
look into kotlin syntax
In pascal there are for x := a to b do ... and for x := a downto b do ...
for var from 1 to 10 with step 3
with step - combined keyword 💀
Keyword that consists of two words
a not in b a is not b
i believe i once wrote not a in b 💀
a better one
for var with values from 1 to 10 with step 3
for var with all values from 1 to 10 with step 3
for var with last 2 values from 1 to 10 with step 3
for var with first 2 values from 1 to 10 with step 3

Maybe just
for var with all values from one to ten with step three
hmm yeah
i think double with is not really good
[32mfor[0m var [32min[0m all values [32mfrom[0m [34mone[0m [32mto[0m [34mten[0m [32mwith step[0m [34mthree[0m
hmm how to color all values
haskell syntax may also be worth considering, [1,3..11] for all odd numbers e.g.
How does this work?
It computes the difference between the second and first element and uses it as step
So it would print 3,5,7,9,11?
Yes
I did say all odd numbers, that's incorrect. [1,3..] is all odd numbers
If you want really in depth syntax, raku has more syntax for ranges than some languages have total. But I wouldn't recommend copying raku, there is a reason it took so long to implement
same as in other languages i guess
for(int i=1; i <= 12; i+=2)
for(i+=2, @arrA.Len()) // same as @(0, arrA.Len()), range(0, len(arrA)) in python
{
Print(arrA[i]);
}
^ i think thats how i will do this in my lang
!e
arrA = [1,2,3,4,5,6]
for i in range(0, len(arrA), 2):
print(arrA[i])
@finite blaze :white_check_mark: Your 3.11 eval job has completed with return code 0.
001 | 1
002 | 3
003 | 5
@ will create an iterator
same
all odd positive integers
fair enough
how to express iterator over all odd numbers from -inf to +inf?
how would it iterate over them 🤔
Like in order?
what is the first odd number?
-sys.maxint maybe. Idk what the mathematicians did before the sys library
if you define inf to be lim(n->∞)(3^n) then inf is odd
you can prove this by induction :)
python has mathematically correct integers, it means that they can be arbitrary long
so there is no such thing as sys.maxint
Lol that was just a joke
there is this: ```py
hex(sys.maxsize)
'0x7fffffffffffffff'
i cant so it must be false ;)
yo, i saw the pep for generic function objects got a go ahead, that's awesome
still hasnt been merged 
Wow this is cool, I haven't seen this yet
anyone have a link? EDIT found it: https://discuss.python.org/t/pep-718-subscriptable-functions/28457
!e
# fib
p=5**.5/2+.5;a=lambda n:round((p**n-(-p)**-n)/5**.5)
b=lambda n:round((5**.5/2+.5)**n/5**.5)
# correctness check
print(all(a(i)==b(i) for i in (__import__("random").randint(0, 1000) for _ in range(100))))
@versed eagle :white_check_mark: Your 3.11 eval job has completed with return code 0.
True
i think i've already seen this before
also that was half a year ago
good old one liners
63 bit integer my beloved
print('\n'*int(o:=(b:=int(input("Pyramid base size: ")))%2==1)+'\n'.join((s:=' '*(b//2-i))+'#'*int(o)+'#'*i*2+s for i in range(b//2+1)))
Hi guys) Is it possible to inherite class from GeneratorType. Are there any tricks?
for i in range((b:=int(input("Pyramid base size: ")))//2+1):print(f"{'#'*(b%2+i+i):^{b}}")
wait you can include variables in f-strings? (like {foo:^{bar}})
yep
well that's -46b
hm?
golfed your code from 136b to 90b
b?
bytes
because this is 74c but 172b ```py
exec(bytes("硜晦硜敦潦湩爠湡敧⠨㩢椽瑮椨灮瑵∨祐慲業慢敳猠穩㩥∠⤩⼩㈯ㄫ㨩牰湩⡴≦❻✣⠪╢⬲⭩⥩帺扻絽⤢",'u16')[2:])
but that is just like just cheating at this point
what is it even doing
exactly
No because then you'd need to marshal load it
Not necessarily
You can create code objects manually
Sure but then you'd notice it in the source code as well, it wouldn't just be exec()
I worked on an open source project that embeds the Mozilla SpiderMonkey JS engine in Python --> https://github.com/Distributive-Network/PythonMonkey
it lets you interoperate with JS and Python really easily and you get some cool extra benefits out of it like being able to run WebAssembly from JS super easily. Since its just a python library, you can install it with pip install pythonmonkey
import pythonmonkey as pm
js_func = pm.eval("(x) => { console.log(`${x} is an awesome number!`); }")
js_func(4) # 4 is an awesome number
PLay around with the colab https://colab.research.google.com/drive/1INshyn0gNMgULQVtXlQWK1QuDGwdgSGZ?usp=sharing
here's like an article I wrote about its alpha release. Figured people in this chat might be interested
Announcing PythonMonkey’s alpha release — use Python code in JavaScript and vice versa with ease and virtually no performance loss!
later down the line we want to evolve it into a Node.JS / Deno / Bun killer that would be like a drop in replacement for node.js that lets you import python modules like esms
I'm gonna be busy driving for the next few hours so apologies if I dont respond until like noon for any questions anyone has
how you write this in the longest form possbile
for i in string:
lst.append(i)
for i in string:
1 and 1 and 1 and 1 and 1 and 1 and 1 and 1 and ... and lst.append(i)
ty
i assume you made a typo with lst and meant list (or better chars)
for i in range(string.__len__.__call__()):
chars.append.__call__(string.__getitem__.__call__(i.real.numerator.__int__.__call__()))
hmm, the code was provided by someone else, making a competition of who can write the longest form of that code
lst could be a variable name, donno
competitions like that are pointless
the only real limit is how much time you're willing to spend making it longer
or how big of a file you're able to store
you could literally just use whitespace
for i in range(string):
lst.append(i)
indentation can be as long as you want it to be
That's not true is it? There's a max indent
Then add newlines.
Also, the maximum indents apply to levels, not amount of spaces IIRC
!e exec('if 1:\n' + ' ' * 10**6 + 'print(123)')
!e exec('if 1:\n' + ' ' * 10**7 + 'print(123)')
!e exec('if 1:\n' + ' ' * 10**8 + 'print(123)')
!e exec('if 1:\n' + ' ' * 3 * 10**7 + 'print(123)')
!e exec('if 1:\n' + ' ' * 2 * 10**7 + 'print(123)')
@fleet bridge :white_check_mark: Your 3.11 eval job has completed with return code 0.
123
hmm, not sure if it's the right channel for this but it probably falls under the 'python weirdness' category so here goes
let's say you have a function that you can't/don't want to change the code of. iter is used inside this function multiple times to create iterators from an arbitrary collection of items. for debug purposes, you want to replace the iterators made in this function with special ones that contain extra info which can help track down bugs.
would it be ok to just remake the function with new globals?
i.e.
def mask_globals(function: types.FunctionType, masked_globals: dict):
return types.FunctionType(
code=function.__code__,
globals={**function.__globals__, **masked_globals},
name=function.__name__,
argdefs=function.__defaults__,
closure=function.__closure__,
)
then
new_function = mask_globals(original_function, {'iter': DebugIterator})
would there be any major pitfalls here?
https://docs.python.org/3/library/unittest.mock.html#unittest.mock.patch this is probably easier, patch can be used as a decorator (though it would not unpatch if that function calls into other functions which then call iter)
how would i specify iter as a target? module.iter doesn't seem to work
in any case, i've already got it working with mask_globals above. just wondering if there are any potential issues i might've overlooked with replacing a function's globals like that.
builtins.iter afaik, but your solution does seem like it would work well enough.
The solution by @brazen geyser is also leaky: if the global state is mutated after this patch, that wouldn't be seen in the patched function.
is there a way to limit the replacement to just inside the function with mock.patch? replacing iter at the builtins level sounds like it could have unintentional negative side effects i.e. some unrelated thing in another thread using iter while the patched function is also running.
ohh true. would a ChainMap work then?
ah
TypeError: function() argument 'globals' must be dict, not ChainMap
so...
class ChainMap(collections.ChainMap, dict):
pass
😃
!e
from types import SimpleNamespace
def esoteric_class(gen):
def wrapper(*args, **kwargs):
g = gen(*args, **kwargs)
next(g)
return SimpleNamespace(**g.gi_frame.f_locals)
return wrapper
@esoteric_class
def User(first_name: str, second_name: str):
full_name: str = f'{first_name} {second_name}'
def is_jack():
return first_name.lower()=='jack'
yield
user = User('Jack', 'Smith')
print(user.is_jack())
@sonic birch :white_check_mark: Your 3.11 eval job has completed with return code 0.
True
Cool 🙂
also this wouldn't work in other way: function cannot mutate global state
!e
from collections import ChainMap
class ChainMap(ChainMap, dict): pass
print(eval('iter', ChainMap({'iter': 123}, globals())))
@brazen geyser :white_check_mark: Your 3.11 eval job has completed with return code 0.
123
this works in 3.10 and above, but in 3.9 and below it seems global lookups use dict.__getitem__ regardless of mro, so rip to that.
this is perfect fit for a project im working on attempting to make pyright's api accessible from python this looks so much nicer than stpyv8, thanks!
fishhook to the rescue!
Awesome! if you want help working with it lmk, or feel free to @ or dm me
i cant dm you without adding you 😉
I will note that the node compatibility isn't perfect at the moment. Although it works with some npm packages like crypto-js its an area for improvement
But you can replace it / use it as a base to get stuff working. for instance I needed to use fs.readFileSync from nodejs so I had to replace it with open in python
But we'll be improving it over time, and honestly it might be the closest python library you can get to running nodejs libraries at the moment even though its only in alpha
is there?
(ignoring physical memory limits)
Cell In[2], line 101
if True:
^
IndentationError: too many levels of indentation```
took a while but you can get there
can you send the entire file?
cause this works on my machine:
exec(f"if True:\n {' ' * 2**32}print('hello world')")
i stacked if True: a lot not just used a tonne of ws
oh
indentation levels
not the actual indentation itself
oh something interesting did happen though
exec(f"if True:\n {' ' * 2**32}print('hello world')")
for this, when using a value of 2**31, i get a system error
it doesnt happen when creating the string, just when exec'ing it
>>> _=f"if True:\n {' '*2**31}print('hello world')"
>>> exec(_)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
SystemError: Negative size passed to PyUnicode_New
creating the string is fine, but exec'ing it does that
but thats not caused by the indentation
hmm
just confirmed 2**33 works fine also
am testing 2**34 now
looks like overflow
yeah
didneylan inspired this
from itertools import permutations as a
p = lambda s,c:'\n'.join(' '.join(''.join(h)for h in g)for g in zip(*([a(s)]*c)))
print(p('disneyland', 10))
!e
from itertools import permutations as a
p = lambda s,c:'\n'.join(' '.join(''.join(h)for h in g)for g in zip(*([a(s)]*c)))
print(p('disneyland', 10))
@fleet bridge :warning: Your 3.11 eval job timed out or ran out of memory.
[No output]
Useful!
Wow 👏
If you're interested in the project - would you mind giving the project a star on GitHub?
I don't want to be too annoying but it really makes a difference!
And if you have any questions about it let me know!
i think i've made this before
Where?
It looks similar but implemented in totally different way. BTW in my case you don't need 'self' but you must call yield in the end
!e
import subprocess
subprocess.check_output(["ls", "-la"])
@sacred sphinx :x: Your 3.11 eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "/home/main.py", line 2, in <module>
003 | subprocess.check_output(["ls", "-la"])
004 | File "/usr/local/lib/python3.11/subprocess.py", line 466, in check_output
005 | return run(*popenargs, stdout=PIPE, timeout=timeout, check=True,
006 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
007 | File "/usr/local/lib/python3.11/subprocess.py", line 548, in run
008 | with Popen(*popenargs, **kwargs) as process:
009 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
010 | File "/usr/local/lib/python3.11/subprocess.py", line 1026, in __init__
011 | self._execute_child(args, executable, preexec_fn, close_fds,
... (truncated - too many lines)
Full output: https://paste.pythondiscord.com/CDW4K7KBJCJ5ZGV33WOUSXDYTU
wrong channel; go to #bot-commands for stuff like that
cool this is actually non destructive https://flatliner.herokuapp.com/
i was actually thinking of making such thing
Me on my way to make a programming teacher quit their job if they tell me to make one more sorting algorithm:
sort = lambda a:[*{*a}]
why manually sort it when you can just convert it to unsorted then convert it back to sorted?
although I'll just check that it works in new versions
!e ```py
a = [1,6,2,5,3,4,9,8,7]
print( a, "sorted is", [*{*a}] )
@floral meteor :white_check_mark: Your 3.11 eval job has completed with return code 0.
[1, 6, 2, 5, 3, 4, 9, 8, 7] sorted is [1, 2, 3, 4, 5, 6, 7, 8, 9]
how does this work?
it doesnt work for numbers bigger than 255 tho
It's a consequence of how set works internally, and is not reliable for larger numbers
it literally converts the type to unsorted set then back to list which is inherently sorted
yes
because they arent allocated in order neccesarily
what would be the shortest way to reverse the order of ^ list?
[::-1]
listing them backwards there was purely coincidental
!e ```py
a = [255, 1530, 510, 1275, 765, 1020, 2295, 2040, 1785]
print([*{*a}][::-1])
@floral meteor :white_check_mark: Your 3.11 eval job has completed with return code 0.
[255, 510, 765, 1020, 1275, 1530, 1785, 2040, 2295]
!e py a = [255, 1530, 510, 1275, 765, 102, 2295, 2040, 1785] print([*{*a}][::-1])
@astral rover :white_check_mark: Your 3.11 eval job has completed with return code 0.
[255, 510, 765, 1275, 1530, 1785, 2040, 2295, 102]
that time at least with different numbers it broke
102 isn't over 255
!e ok then what about this
a = [255, 1530, 510, 1275, 765, 1022, 2295, 2040, 1785]
print([*{*a}][::-1])```
@astral rover :white_check_mark: Your 3.11 eval job has completed with return code 0.
[255, 510, 765, 1275, 1530, 1785, 2040, 2295, 1022]
in which case what we can do is test for being under 255 and list them first then concatenate the backwards sort for over 255
no you cant
so why did this work?
random chance
it seems to work a little over 255
it doesn't work either way for arbitrary integers though like keyboard mashed ones:
you can't rely on it for anything version-agnostic
was this made before walrus?
[with open('test') as f:
print(f) for __builtins__.__import__ in [None]][0]
got that output from it
so uh
yeah
!e
sort = lambda a:(n:=[])or(any(n.extend((v,)*a.count(v))for(v)in{*a}))or(type(a)(n))
print(sort(__import__("random").shuffle((__:=list(range(10000))))or __) == sorted(__))
@versed eagle :white_check_mark: Your 3.11 eval job has completed with return code 0.
True
works up to 10000 ¯_(ツ)_/¯
thats probably cause they got allocated in order
interesting
so you're saying its a valid sorting algorithm, as long as you preallocate all the numbers in a sorted manner
i think so
thats actually really interesting
i dont think thats the case
cause im able to allocate certain numbers beforehand and still get them returned sorted
both numbers ive tested it on, 3005 and 10005
repl (cause im lazy), but im using larger numbers each time, so it shouldnt care about previous tests
first test was range(10000) after preallocating 3005, second test was range(10010) after preallocating 10005
i went ahead and wrote a script
TEST_PREALLOCS = tuple(map(list, (
range(250, 300),
range(3000, 4000),
)))
import random
sort = lambda a:(n:=[])or(any(n.extend((v,)*a.count(v))for(v)in{*a}))or(type(a)(n))
testlist = list(range(10000))
random.shuffle(testlist)
print(sort(testlist) == sorted(testlist))
this prints True for me
its true up to 100_000 as well
it breaks as soon as negative numbers are involved but thats not important
for non-negative integers up to 100_000, it is a correct (albeit (extremely) slow) sorting algorithm
probably until higher numbers also, but i dont have time to test this anymore since i have to go now
int.__hash__ just returns number itself, it dont depend on id
except for -1 right?
Well in CPython it technically is the number mod 2^61 - 1.
!e
x = 2**61 - 1
print(hash(1000 * x + 127))
print(hash(-1000 * x - 127))
print(hash(x))
@distant salmon :white_check_mark: Your 3.11 eval job has completed with return code 0.
001 | 127
002 | -127
003 | 0
Which is one reason why hash tables with integer keys in Python are super fragile. You can easily create tons of integers with the same exact hash.
pretty rare to accidentally create a whole bunch of numbers with this pathologic hash though
Accidentally yes, but it makes it really easy to "attack" python scripts
Most programmers just assume that it is safe to store integers in set or to use integers as keys in a dict, but it is in fact not safe.
i feel like just the memory needed to store that many numbers (of that size, especially) would be an issue long before it slows down the dict
That is not true. The memory needed is tiny compared to how slow hash tables become
mm
is this a dos attack?
yes
!e
x = 2**61 - 1
[x * i for i in range(10**5)]
@distant salmon :warning: Your 3.11 eval job has completed with return code 0.
[No output]
!e
x = 2**61 - 1
set(x * i for i in range(10**5))
@distant salmon :warning: Your 3.11 eval job timed out or ran out of memory.
[No output]
Interesting
ooh, neat
It is also possible to construct ddos hashtable attacks with carefully designed smaller numbers too. But the easy "attack" is just to use multiples of 2^61 - 1
>>> s = set(x * i for i in range(10**5))
>>> sys.getsizeof(s)
2097260
>>> l = list(x * i for i in range(10**5))
>>> sys.getsizeof(l)
439916
the set actually runs pretty quick on my computer
What value of x did you use?
same as yours, x = 2**61 - 1
Can you try printing hash(x)?
mm i wonder if my python is using a different mod
yea ill check that
ah, yep
>>> hash(x)
1073741823
>>> hash(x * 2)
2147483646
>>> hash(x * 3)
1073741822
>>> x = 2 ** 124 - 1
>>> hash(x)
0
>>> hash(x * 2)
0
124 apparently
and yep, this doesnt seem like it'll finish running anytime soon
2^31 - 1 divides 2^124 - 1 cause of difference of squares rule
So try 2^31 - 1
fwiw ```py
In [1]: import sys
In [2]: sys.hash_info
Out[2]: sys.hash_info(width=64, modulus=2305843009213693951, inf=314159, nan=0, imag=1000003, algorithm='siphash24', hash_bits=64, seed_bits=128, cutoff=0)
In [3]: import math
In [4]: math.log2(sys.hash_info.modulus)
Out[4]: 61.0
ah that's it
So my point still stands, hash tables in Python are very fragile
wonder if there are any other data structure hashes that are easy enough to mess with
tuple?
the tuple/list hashes are pretty messy from what i remember
String hashes in Python are randomized, so you will be safe using them as keys in hash tables
I dont remember exact formula, but i believe it is simple enough no, it's not
the old one was just a multiplication and xor, but a simple collision was found so they changed it to make it more complex
iirc the issue was that (a, (a, b)) would have the same hash as b
Btw if someone is interested to know how dict works in Python, then here is the relevant source code. https://github.com/python/cpython/blob/bb86bf4c4eaa30b1f5192dab9f389ce0bb61114d/Objects/dictobject.c#L700-L720
From this, it is possible to figure out how to construct sequences of small numbers (say < 1e6) that can be used to attack dict
tuple.__hash__ is located here: https://github.com/python/cpython/blob/main/Objects/tupleobject.c#L290-L304
static Py_hash_t
tuplehash(PyTupleObject *v)
{
Py_ssize_t i, len = Py_SIZE(v);
PyObject **item = v->ob_item;
Py_uhash_t acc = _PyHASH_XXPRIME_5;
for (i = 0; i < len; i++) {
Py_uhash_t lane = PyObject_Hash(item[i]);
if (lane == (Py_uhash_t)-1) {
return -1;
}
acc += lane * _PyHASH_XXPRIME_2;
acc = _PyHASH_XXROTATE(acc);
acc *= _PyHASH_XXPRIME_1;
}
/* Add input length, mangled to keep the historical value of hash(()). */
acc += len ^ (_PyHASH_XXPRIME_5 ^ 3527539UL);
if (acc == (Py_uhash_t)-1) {
return 1546275796;
}
return acc;
}
looks pain
The len being mixed in at the end there makes it a little more annoying
mm actually it seems pretty doable 👀
acc = _PyHASH_XXROTATE(acc);
acc *= _PyHASH_XXPRIME_1;
the bit rotation is obviously invertible
the multiplication is invertible (even with bit overflow) because it's multiplying by a prime
then invert the acc += lane * _PyHASH_XXPRIME_2; to get lane, which is the hash of the element
and we can just set the element equal to lane, since ints hash to themselves
x = hash(obj)
assert x == hash(x) # always true
(hash is an identity function if applied the second time)
idempotent! i love that word
I forgot that word (
@languid hare :white_check_mark: Your 3.11 eval job has completed with return code 0.
001 | 5740354900026072187
002 | 5740354900026072187
!e
assert hash(()) == hash((1778577755476246539,)) == hash((1778577755476246539, 654455456954384049)) == hash((1778577755476246539, 654455456954384049, -930367412484010356)) == hash((1778577755476246539, 654455456954384049, -930367412484010356, -2054489711005872846))
@languid hare :warning: Your 3.11 eval job has completed with return code 0.
[No output]
trying to extend it once more fails though, interestingly
What are those Numbers? Where do they come from
Python Golfing :D hehe
I have a script, which turns any integer (no limit as long as the RAM is enough) into a worded number in German, following all the capitalization rules and everything. For fun I was golfing the script, so it it as short as possible (counting bytes, I don't like those exec(decode()) approaches).
And so I could make it as small as 1040 bytes (wc -c zahl.py)
Would anyone be ready to break their brain with me and make the code sub 1000?
*A,L,M,J,K,N,O,P,B,D,E,G,g,k="drei vier fünf sech sieb acht neun zehn elf zwölf sept quin ns ginta ing okt qua mn mx dr non tre en".split()
F,n,o,C,U,m,H,j="ego sanz"
I,S="",10;Q=S*S;T=Q*S
Y=["null","ein","zwei"]+A*T
Y[6]+=U;Y[7]+=k
def Z(q,r=I):
a=abs(q);d,u=a//T,2;s=Y[b:=a%Q]*(a<S)or Z(d%T,"tausend ")+Z(a%T//Q,"hundert")+[Z(a%S,"und")+["zwan",*A][b//S-2]+"zß"[b//S==3]+"ig",Y[b]*(b>0)+A[7]*(b>12)][b<20]
while d:
V,v=u//2,I
while V:c,e=V//S%S,V//Q%S;h,z,*_=[I,I,"un",I,"duo",I,g,U,P+"ttuor",I,M,I,U+F,"x",L+F,B,O+o,I,"nove",B][V%S*2:];f,y,*_=[i:=["nxs",j,H,"duz",J,g+j,J,P+E+N,J,M+n,H,"sesz",H,L+N,D,O+N,I,G+n,I][e*2-2:],[H,"dezi","ms","viginti",J,"tri"+K,J,P+E+m+K,J,M+P+K,H,"sexa"+K,H,L+"ua"+K,D,O+o+K,I,G+m+K][c*2-2:]][c>0];v=(*[H,*"mb","tr",P+E,M+"t","sext",L,O,G][V%T:],(h+I.join({*z}&{*f})+y+i[1]*(c>0)+"enti"*(e>0))[:-1])[0]+"illi"+v;V//=T
d//=T;s=Z(p:=d%T,F*(p<2)+C+v.title()+[o+H,"arde"][u%2]+k[u%2:]*(p>1)+C)+s;u+=1
return[(s:="minus "*(q<0)+s+U[(a==1)*r>I:b==1])[0].title()+s[1:].strip(),(s+r)*(a>0)][r>I]
zahl=Z
The rules are: the file must have a function "zahl" which can be imported from another file and the function should accept an integer as its' first argument anf return a string representing the number
The code above is producing the right output and is following the rules.
Uh, better not show it, it's really bad. It was written to be golfed later on
im extending each of the tuples such that their hash remains the same
The problem is that your code is kinda unreadable as it is right now, so it wont be easy to help codegolf it down. For example it would be nice to look at a version that doesn't use ;
following all the capitalisation rules and everything
wdym by that?
or rather, can you specify what rules you're talking about
also, what python version does this run in
seems to work in 3.11 for me
It looks like just python3, I dont think it uses any newer feature
>>> print(Z(1234567))
Eine Million zweihundertvierunddreißigtausend fünfhundertsiebenundsechzig
Here is another example
>>> print(Z(1234567123456712345671234567))
Eine Quadrilliarde zweihundertvierunddreißig Quadrillionen fünfhundertsiebenundsechzig Trilliarden einhundertdreiundzwanzig Trillionen vierhundertsechsundfünfzig Billiarden siebenhundertzwölf Billionen dreihundertfünfundvierzig Milliarden sechshunderteinundsiebzig Millionen zweihundertvierunddreißigtausend fünfhundertsiebenundsechzig
im asking because i want to know if we can use things like 12in something
Yeah, I am using 3.11 for this, so - no we can't, this will produce a Warning
This version is already golfed, so I am asking about any further golfing possibilities
technically SyntaxWarning is sent to stderr so it doesn't matter if you only care about stdout
The grammar rules for german numbers
The original code was over 2600 bytes, btw
Can't even send here
can you elaborate on what those are
i dont know german
okie so ~somebool is still fine though
yay
Well, it is a bit complicated, here is the Wikipedia for this tho: https://de.m.wikipedia.org/wiki/Zahlennamen
In diesem Artikel geht es um den Aufbau von Zahlennamen und die Benennung von Zahlen im Dezimalsystem. Diese Namen wurden 2001 bei der mathematischen Sitzung in Berlin festgelegt.
that is german wikipedia, written in german
i dont know german
Although, I believe those rules won't help any further - just looking at the code should suffice for golfing
Wait a second, I will host the ungolfed version somewhere, so I can share it here
!paste
If your code is too long to fit in a codeblock in Discord, you can paste your code here:
https://paste.pythondiscord.com/
After pasting your code, save it by clicking the Paste! button in the bottom left, or by pressing CTRL + S. After doing that, you will be navigated to the new paste's page. Copy the URL and post it here so others can see it.
The issue is that the code golfed in such a way that makes it kinda unreadable. Like the prolific use of ; and , . My codegolf intunition says that there likely is a ton of improvements you could do, but this version is just super unreadable.
Well, how can I make it readable tho, if I still wanna keep it short?
Ofc I can paste it here formatted and made more readable, but that would cost me a lot of bytes
So you guys are not like those weirdos (like me) who understand gibberish code? I guess, I will be better on myself then
Golfed code and readability are like two different universes
its quicker to ask for readable code than to take time to understand less readable code
its fairly challenging to golf something if you have no idea what it's doing
there are plenty of things which might have side effects that can change things, or might not
In codegolfing you can sometimes get stuck in a local mimimums. So unless you know your approach is really good, you don't want to try to optimize it 100%. I think that keeping the formatting readable here would help a ton.
Okey, I will go and re-write my current code, to make it more readable, I hope we can find some solutions then
you don't have to rewrite it, necessarily
(And I will add explanatory comments)
I mean, just adding more whitespace and stuff
ah
well, send a ping whenever you do that ig
btw this version has unneeded recursion
im pretty sure just iterating would be shorter (plus more efficient lmao)
Iterating is what is happening in the final code
Also final code does not have any functions excepts the main function "Z"
Commenting my code to make clear, what is happening just made it double the ungolfed size
Would customizing import behavior fall under this channel's topics, or is it not weird enough?
If it doesnt, which channel would be better?
-1b ```py
A,L,M,J,K,N,O,P,B,D,E,G,g,k="drei vier fünf sech sieb acht neun zehn elf zwölf sept quin ns ginta ing okt qua mn mx dr non tre en".split()
F,n,o,C,U,m,H,j="ego sanz"
I,S="",10;Q=SS;T=QS
Y=["null","ein","zwei"]+AT
Y[6]+=U;Y[7]+=k
def Z(q,r=I):
a=abs(q);d,u=a//T,2;s=Y[b:=a%Q](a<S)or Z(d%T,"tausend ")+Z(a%T//Q,"hundert")+[Z(a%S,"und")+["zwan",A][b//S-2]+"zß"[b//S==3]+"ig",Y[b](b>0)+A[7](b>12)][b<20]
while d:
V,v=u//2,I
while V:c,e=V//S%S,V//Q%S;h,z,_=[I,I,"un",I,"duo",I,g,U,P+"ttuor",I,M,I,U+F,"x",L+F,B,O+o,I,"nove",B][V%S2:];f,y,_=[i:=["nxs",j,H,"duz",J,g+j,J,P+E+N,J,M+n,H,"sesz",H,L+N,D,O+N,I,G+n,I][e2-2:],[H,"dezi","ms","viginti",J,"tri"+K,J,P+E+m+K,J,M+P+K,H,"sexa"+K,H,L+"ua"+K,D,O+o+K,I,G+m+K][c2-2:]][c>0];v=([H,"mb","tr",P+E,M+"t","sext",L,O,G][V%T:],(h+I.join({z}&{f})+y+i[1](c>0)+"enti"(e>0))[:-1])[0]+"illi"+v;V//=T
d//=T;s=Z(p:=d%T,F(p<2)+C+v.title()+[o+H,"arde"][u%2]+k[u%2:](p>1)+C)+s;u+=1
return[(s:="minus "(q<0)+s+U[a==1!=r>I:b==1])[0].title()+s[1:].strip(),(s+r)*(a>0)][r>I]
zahl=Z
in the thing you sent it recurses
tl;dr i want to customize import behavior to detect and replace methods matching a certain behavior as part of pyglet's unit tests
this does not play nicely with a module proxy class they use:
@fixture(autouse=True)
def monkeypatch_all_shaders(monkeypatch, get_dummy_shader_program):
original_import = __import__
def import_and_patch_shader_getter_members(module_name, *args, **kwargs):
imported = original_import(module_name, *args, **kwargs)
for member_name, member in inspect.getmembers(imported, is_shader_getter):
monkeypatch.setattr(f"{module_name}.{member_name}", get_dummy_shader_program)
return imported
monkeypatch.setattr(builtins, "__import__", import_and_patch_shader_getter_members)
hook __import__
Yes, one-level recursion, but the mechanics you spoke about (unneeded recursion) is implemented as iteration in the golfed version
What change?
(a==1)*r>I -> a==1!=r>I
can you please be more specific? I think that's what I'm trying to do in the code abbove, but I think I am doing it wrong. Others have reported it working, but it may not work in the specific context.
source-level transformation is uncessary imo, just monkeypatching
surely there's a better way to do this py [o+H,"arde"][u%2]+k[u%2:]*(p>1) ```
p>1 u%2 R
T 1 arden
T 0 onen
F 1 arde
F 0 on
amazing, thx
I was sitting on this for a while
-2b ```py
A,L,M,J,K,N,O,P,B,D,E,G,g,k="drei vier fünf sech sieb acht neun zehn elf zwölf sept quin ns ginta ing okt qua mn mx dr non tre en".split()
F,n,o,C,U,m,H,j="ego sanz"
I,S="",10;Q=SS;T=QS
Y=["null","ein","zwei"]+AT
Y[6]+=U;Y[7]+=k
def Z(q,r=I):
a=abs(q);d,u=a//T,2;s=Y[b:=a%Q](a<S)or Z(d%T,"tausend ")+Z(a%T//Q,"hundert")+[Z(a%S,"und")+["zwan",A][b//S-2]+"zß"[b//S==3]+"ig",Y[b](b>0)+A[7](b>12)][b<20]
while d:
V,v=u//2,I
while V:c,e=V//S%S,V//Q%S;h,z,_=[I,I,"un",I,"duo",I,g,U,P+"ttuor",I,M,I,U+F,"x",L+F,B,O+o,I,"nove",B][V%S2:];f,y,_=[i:=["nxs",j,H,"duz",J,g+j,J,P+E+N,J,M+n,H,"sesz",H,L+N,D,O+N,I,G+n,I][e2-2:],[H,"dezi","ms","viginti",J,"tri"+K,J,P+E+m+K,J,M+P+K,H,"sexa"+K,H,L+"ua"+K,D,O+o+K,I,G+m+K][c2-2:]][c>0];v=([H,"mb","tr",P+E,M+"t","sext",L,O,G][V%T:],(h+I.join({z}&{f})+y+i[1](c>0)+"enti"(e>0))[:-1])[0]+"illi"+v;V//=T
d//=T;s=Z(p:=d%T,F(p<2)+C+v.title()+"aorndeenn"[~u&1:4*(p>1)+4:2]+C)+s;u+=1
return[(s:="minus "(q<0)+s+U[a==1!=r>I:b==1])[0].title()+s[1:].strip(),(s+r)(a>0)][r>I]
zahl=Z
You really did golf that, amazing
Sadly, this fails
(p>1) and u%2==1 produces "arde" instead of "arden" and
(p<2) and u%2==1 produces just "ar"
Challenge: get all possible SyntaxWarning kinds without looking into cpython repo.
I will start:```py
1) * object is not callable; perhaps you missed a comma?
()()
<stdin>:1: SyntaxWarning: 'tuple' object is not callable; perhaps you missed a comma?
<stdin>:1: SyntaxWarning: 'list' object is not callable; perhaps you missed a comma?
''()
<stdin>:1: SyntaxWarning: 'str' object is not callable; perhaps you missed a comma?
...()
<stdin>:1: SyntaxWarning: 'ellipsis' object is not callable; perhaps you missed a comma?
{}()
<stdin>:1: SyntaxWarning: 'dict' object is not callable; perhaps you missed a comma?
{()}()
<stdin>:1: SyntaxWarning: 'set' object is not callable; perhaps you missed a comma?
1()
<stdin>:1: SyntaxWarning: 'int' object is not callable; perhaps you missed a comma?
1.()
<stdin>:1: SyntaxWarning: 'float' object is not callable; perhaps you missed a comma?
1j()
<stdin>:1: SyntaxWarning: 'complex' object is not callable; perhaps you missed a comma?
True()
<stdin>:1: SyntaxWarning: 'bool' object is not callable; perhaps you missed a comma?
b''()
<stdin>:1: SyntaxWarning: 'bytes' object is not callable; perhaps you missed a comma?
2) * object is not subscriptable; perhaps you missed a comma?
True[1]
<stdin>:1: SyntaxWarning: 'bool' object is not subscriptable; perhaps you missed a comma?
1[2]
<stdin>:1: SyntaxWarning: 'int' object is not subscriptable; perhaps you missed a comma?
...[1]
<stdin>:1: SyntaxWarning: 'ellipsis' object is not subscriptable; perhaps you missed a comma?
...[...]
<stdin>:1: SyntaxWarning: 'ellipsis' object is not subscriptable; perhaps you missed a comma?
3) * indices must be integers or slices, not *; perhaps you missed a comma?
[][[]]
<stdin>:1: SyntaxWarning: list indices must be integers or slices, not list; perhaps you missed a comma?
()[()]
<stdin>:1: SyntaxWarning: tuple indices must be integers or slices, not tuple; perhaps you missed a comma?
better challenge: get all the warnings in a single line
💀 ```py
''(),''(),''(),''(),''(),''(),''(),''(),''(),''(),''(),''(),''(),
<stdin>:1: SyntaxWarning: 'str' object is not callable; perhaps you missed a comma?
<stdin>:1: SyntaxWarning: 'str' object is not callable; perhaps you missed a comma?
<stdin>:1: SyntaxWarning: 'str' object is not callable; perhaps you missed a comma?
<stdin>:1: SyntaxWarning: 'str' object is not callable; perhaps you missed a comma?
<stdin>:1: SyntaxWarning: 'str' object is not callable; perhaps you missed a comma?
<stdin>:1: SyntaxWarning: 'str' object is not callable; perhaps you missed a comma?
<stdin>:1: SyntaxWarning: 'str' object is not callable; perhaps you missed a comma?
<stdin>:1: SyntaxWarning: 'str' object is not callable; perhaps you missed a comma?
<stdin>:1: SyntaxWarning: 'str' object is not callable; perhaps you missed a comma?
<stdin>:1: SyntaxWarning: 'str' object is not callable; perhaps you missed a comma?
<stdin>:1: SyntaxWarning: 'str' object is not callable; perhaps you missed a comma?
<stdin>:1: SyntaxWarning: 'str' object is not callable; perhaps you missed a comma?
<stdin>:1: SyntaxWarning: 'str' object is not callable; perhaps you missed a comma?
# invalid * literal
5or[]
0xfor[]
0o5or[]
0b1or[]
you could do this to anything tbh ```pycon
()[...]
<stdin>:1: SyntaxWarning: tuple indices must be integers or slices, not ellipsis; perhaps you missed a comma?
()[1.]
<stdin>:1: SyntaxWarning: tuple indices must be integers or slices, not float; perhaps you missed a comma?
()[{}]
<stdin>:1: SyntaxWarning: tuple indices must be integers or slices, not dict; perhaps you missed a comma?
this is beautiful
hmm ive found the issue
rather annoyingly the int hash modulus is slightly smaller than the true range of hashes (on a 64 bit width hash at least)
so on my IDLE python (32 bit width) my script is able to output this:
() 750394483
(-1099103383,) 750394483
(-1099103383, 1675715659) 750394483
(-1099103383, 1675715659, 1507789696) 750394483
(-1099103383, 1675715659, 1507789696, 1437192654) 750394483
(-1099103383, 1675715659, 1507789696, 1437192654, 1048715906) 750394483
(-1099103383, 1675715659, 1507789696, 1437192654, 1048715906, -1487580131) 750394483
(-1099103383, 1675715659, 1507789696, 1437192654, 1048715906, -1487580131, -639358311) 750394483
(-1099103383, 1675715659, 1507789696, 1437192654, 1048715906, -1487580131, -639358311, 518373792) 750394483
(-1099103383, 1675715659, 1507789696, 1437192654, 1048715906, -1487580131, -639358311, 518373792, 129897044) 750394483
(-1099103383, 1675715659, 1507789696, 1437192654, 1048715906, -1487580131, -639358311, 518373792, 129897044, 1888568303) 750394483
and so on
each of the tuples has the same hash as the empty tuple
but on a 64 bit width:
() 5740354900026072187
(1778577755476246539,) 5740354900026072187
(1778577755476246539, 654455456954384049) 5740354900026072187
(1778577755476246539, 654455456954384049, -930367412484010356) 5740354900026072187
(1778577755476246539, 654455456954384049, -930367412484010356, -2054489711005872846) 5740354900026072187
(1778577755476246539, 654455456954384049, -930367412484010356, -2054489711005872846, -3639312580444267251) 8877380592658120617
(1778577755476246539, 654455456954384049, -930367412484010356, -2054489711005872846, -3639312580444267251, 681202749096285504) 8877380592658120617
# initial examples:
>>> ()()
<stdin>:1: SyntaxWarning: 'tuple' object is not callable; perhaps you missed a comma?
>>> 0[0]
<stdin>:1: SyntaxWarning: 'int' object is not subscriptable; perhaps you missed a comma?
>>> [][[]]
<stdin>:1: SyntaxWarning: list indices must be integers or slices, not list; perhaps you missed a comma?
# @<class 'cereal'> (fast):
>>> 5or[]
<stdin>:1: SyntaxWarning: invalid decimal literal
yeah, so let's count message kinds, not exact messages
>>> class H:
... def __hash__(self): return 2**61
...
>>> H()
<__main__.H object at 0x000001DF3B4D69A0>
>>> hash(H())
2305843009213693952
>>> 2**61
2305843009213693952
>>> hash(2**61)
1
which also implies that this is only correct on 32 bit hashes
>>> class H:
... def __hash__(self): return 2**61
...
>>> H()
<H at 0x001FDFF56A1D0>
>>> hash(H())
2_305_843_009_213_693_952
>>> 2**61
2_305_843_009_213_693_952
>>> hash(2**61)
1
>>> x = 2**61
>>> hash(x)
1
>>> sys.hash_info.width
64
``` what is happening? i dont understand
is __hash__ wrapper cutting value to only 32 bits?
or something
im blind, its other way around
int.__hash__ for some reason cuts value to 32 bits?
https://docs.python.org/3/library/stdtypes.html#hashing-of-numeric-types
Essentially, this function is given by reduction modulo P for a fixed prime P. The value of P is made available to Python as the modulus attribute of sys.hash_info.
CPython implementation detail: Currently, the prime used is P =
2**31 - 1on machines with 32-bit C longs and P =2**61 - 1on machines with 64-bit C longs.
ah, that is weird
so there is two limits:
- 64 bits for all hashes
- 61 bits for numeric hashes
@languid hare :white_check_mark: Your 3.11 eval job has completed with return code 0.
001 | 6909455589863252355
002 | 2297769571435864453
that implies that number's hashes can have 8 (2**3) times less different values, so hash collisions on numbers are 8 times more likely
>>> x = (2,); (x,hash(x),hash(hash(x)))
((2,), 6_909_455_589_863_252_355, 2_297_769_571_435_864_453)
>>> x = (1,); (x,hash(x),hash(hash(x)))
((1,), -6_644_214_454_873_602_895, -2_032_528_436_446_214_993)
>>> x = (0,); (x,hash(x),hash(hash(x)))
((0,), -8_753_497_827_991_233_192, -1_835_968_800_350_151_339)
>>> x = (3,); (x,hash(x),hash(hash(x)))
((3,), -5_029_647_727_744_300_836, -417_961_709_316_912_934)
>>> x = (4,); (x,hash(x),hash(hash(x)))
((4,), 8_524_022_316_992_554_414, 1_606_493_289_351_472_561)
>>> x = (5,); (x,hash(x),hash(hash(x)))
((5,), -7_813_438_383_599_366_905, -895_909_355_958_285_052)
>>> x = (6,); (x,hash(x),hash(hash(x)))
((6,), -1_305_797_627_497_368_480, -1_305_797_627_497_368_480)
``` only for 1/8 of tuples of kind `(n,)` this would be true: `hash(x) == hash(hash(x))`
!e ```py
n = 10**6
print(sum(hash((n,)) == hash(hash((n,))) for n in range(n))/n)
@fleet bridge :white_check_mark: Your 3.11 eval job has completed with return code 0.
0.250033
it is 1/4, not 1/8...
yeah, there is some off-by-one error in my calculations
For a complex number z, the hash values of the real and imaginary parts are combined by computing
hash(z.real) + sys.hash_info.imag * hash(z.imag), reduced modulo2**sys.hash_info.widthso that it lies inrange(-2**(sys.hash_info.width - 1), 2**(sys.hash_info.width - 1)). Again, if the result is -1, it’s replaced with -2.
hmm i might be able to bypass it with complex
the components would be subject to the 61 bit limit but the overall hash is 64
weird
!e
import sys
print(sys.hash_info.imag)
@languid hare :white_check_mark: Your 3.11 eval job has completed with return code 0.
1000003
neat, another prime
at some day cpython will use all primes for some calculations...
sys.hash_info.inf = 314159 is prime too
in other news ive just learnt of a float method from the hash docs
!e
from math import pi
num, denom = pi.as_integer_ratio()
print(f"pi = {num}/{denom}")
@languid hare :white_check_mark: Your 3.11 eval job has completed with return code 0.
pi = 884279719003555/281474976710656
pi is rational, who knew
@languid hare :white_check_mark: Your 3.11 eval job has completed with return code 0.
001 | 5
002 | 4
003 | 3
makes sense
!e
class H:
def __init__(self, v):
self.v = v
def __hash__(self):
return self.v
print(hash(H(2**63)))
print(hash(H(2**63-1)))
print(hash(H(2**63+1)))
@languid hare :white_check_mark: Your 3.11 eval job has completed with return code 0.
001 | 4
002 | 9223372036854775807
003 | 5

what are you goons up to today
cpython hashes ints weirdly
the fuck lol
mm might be something about the internal representation
2^63-1 is the largest number that can be stored in 8 bytes, after that the python long object needs another 8 bytes
(hypothesis. ive no idea what the internal representation looks like :p)
!e
class H:
def __init__(self, v):
self.v = v
def __hash__(self):
return self.v
print(hash(H(2**31-1)))
print(hash(H(2**31)))
print(hash(H(2**31+1)))
@languid hare :white_check_mark: Your 3.11 eval job has completed with return code 0.
001 | 2147483647
002 | 2147483648
003 | 2147483649
(if you're wondering, yes the H object is necessary because of this)
for 32 bit systems maybe?
its been a while since i read the source for longobject
Objects/longobject.c line 3297
long_hash(PyLongObject *v)```
isn't that 8 bytes
i can do math 
time to unwrap my 32-bit python 3.10 🥳
Python 3.10.0 (tags/v3.10.0:b494f59, Oct 4 2021, 18:46:30) [MSC v.1929 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> class H:
... def __init__(self, v):
... self.v = v
... def __hash__(self):
... return self.v
...
>>> print(hash(H(2**31-1)))
2147483647
>>> print(hash(H(2**31)))
1
>>> print(hash(H(2**31+1)))
2
>>> import sys
>>> sys.hash_info
sys.hash_info(width=32, modulus=2147483647, inf=314159, nan=0, imag=1000003, algorithm='siphash24', hash_bits=64, seed_bits=128, cutoff=0)
!e
import sys
print(sys.hash_info)
@languid hare :white_check_mark: Your 3.11 eval job has completed with return code 0.
sys.hash_info(width=64, modulus=2305843009213693951, inf=314159, nan=0, imag=1000003, algorithm='siphash13', hash_bits=64, seed_bits=128, cutoff=0)
also python arbitrary length integers only either fit 4 or 2 bytes
✨
so an 8-byte integer would have to use 3-5 internal digits of space
ugh i just wanted to make tuples that cant be put into a dict
now i gotta deal with this mess
so it's not something with the internal representation i think
this can be explained here https://github.com/python/cpython/blob/main/Objects/typeobject.c#L8758-L8770
Objects/typeobject.c lines 8758 to 8770
/* Transform the PyLong `res` to a Py_hash_t `h`. For an existing
hashable Python object x, hash(x) will always lie within the range of
Py_hash_t. Therefore our transformation must preserve values that
already lie within this range, to ensure that if x.__hash__() returns
hash(y) then hash(x) == hash(y). */
h = PyLong_AsSsize_t(res);
if (h == -1 && PyErr_Occurred()) {
/* res was not within the range of a Py_hash_t, so we're free to
use any sufficiently bit-mixing transformation;
long.__hash__ will do nicely. */
PyErr_Clear();
h = PyLong_Type.tp_hash(res);
}```
mm
!e
tuples = [(4, -959703335196834730), (7, -1649852810609714918), (8, 403413395588177987), (11, 28542440325690448), (12, 2081808646523583353), (14, -661607035087189740), (15, 1391659171110703165), (18, 1016788215848215626), (42, -824914778392737540), (45, -1515064253805617728), (46, 538201952392275177)]
for t in tuples:
print(t, "=>", hash(t))
@languid hare :white_check_mark: Your 3.11 eval job has completed with return code 0.
001 | (4, -959703335196834730) => 123456789
002 | (7, -1649852810609714918) => 123456789
003 | (8, 403413395588177987) => 123456789
004 | (11, 28542440325690448) => 123456789
005 | (12, 2081808646523583353) => 123456789
006 | (14, -661607035087189740) => 123456789
007 | (15, 1391659171110703165) => 123456789
008 | (18, 1016788215848215626) => 123456789
009 | (42, -824914778392737540) => 123456789
010 | (45, -1515064253805617728) => 123456789
011 | (46, 538201952392275177) => 123456789
... (truncated - too many lines)
Full output: https://paste.pythondiscord.com/IQ47TGCNC5QO4HVN72Z7MWGZBM
partially working
why?
8 bytes fits into 2 32-bit integers no?
unless its preallocating to have more space than it needs, which i dont think it does
but it doesn't fit into 2 integers that fit in range(2**30)
there's always an extra 2 bytes not used in the digits (1 byte for 16-bit digits) and i have no idea why
what
python uses uint32_t for 64 bit systems
so it works for 1/4 of all integers?
mhm
by default for 32-bit systems too iirc
since python 3.11 or 3.12
oh
*bits, not bytes
my last step is finding an element with a given hash, which at the moment i assume to be the hash itself.
however that only works 1/4 of the time as we've seen
mm yeah
last i checked it was a uint16_t for 32 bit systems
in python 3.10 and below, yes
but they made that architecture-independent and defaulted to uint32_t for both 32-bit and 64-bit systems
ig i need to go read the newest version then
!e ```py
from ctypes import c_uint
n=2**63-1;print([(c_uint3).from_address(id(n)+int.basicsize)])
@quartz wave :white_check_mark: Your 3.11 eval job has completed with return code 0.
[1073741823, 1073741823, 7]
>>> hex(1073741823)
'0x3fffffff'
why use 2**30?
does that not just waste 2 bits
or do they use those for something else?
no idea
maybe try to avoid overflow with signed integers
so basically a carry register?
specifically
long_hash()requires thatPyLong_SHIFTis strictly less than the number of bits in anunsigned long, as do thePyLong<->long(orunsigned long) conversion functions
and
the marshal code currently expects that
PyLong_SHIFTis a multiple of 15
here's something interesting to run on xonsh
espeak @(' '.join(random.choices([l + 'oink' for l in alpha], k=100)))
gdi complex converts its compontents to floats when they get too big
Just thinking.. can it be somehow useful?
from functools import partial as part
@part(part, b=5)
def foo(a, b):
print(a, b)
My brain is not smart enough to understand this
from functools import partial as part
def foo(a, b):
print(a, b)
foo = part(foo)(part(b=5))```
yeah that doesnt even help, i can guess the meaning but lol
foo=print does the same
So:
foo = part(print)(part(b=5))
Oh, wait, no. Print doesn't have b kwarg
Nvm
huh
how do you get that
is that not the correct deco order?
from functools import partial as part
@part(part, b=5)
def foo(a, b):
print(a, b)
foo = part(part, b=5)(foo) # = part(foo, b=5)
i like it
mm last ditch effort: nest tuples until it happens to be small enough
lol
on average itll need 4 nested tuples to achieve a target hash
since its only within range 1/4 of the time
The only use case I can see so far is - expensive computation, like:
from functools import partial as part
def long_compute():
# some expensive work
@part(part, b=long_compute())
def foo(a, b):
# do something with a and b
If you want b to be initialized before the function call, but you don't want somebody outside to be able to change b after initialized
UDP:
Examples:
1.
b=...#bad, b can be changed from outside
def foo(a):
# use a, b
def foo(a, b=...): # bad, b can be changed by caller
# use a, b
def foo(a):
# b = ... # bad, b will be re-init each time function is called. Not good for long init
that's a nice use
!e i'm not sure i'm getting your point here ```py
from functools import partial as part
def long_compute():
return 2
@part(part, b=long_compute())
def foo(a, b):
return a + b
print(foo(5, b=4)) # SHOULD be 7
@quartz wave :white_check_mark: Your 3.11 eval job has completed with return code 0.
9
it's like using a default value
now if partial() was just ```py
def partial(*args, **kwargs):
f, *args = args
return lambda *a, **k: f(*args, *a, **kwargs, **k)
Damn, you are right
That is actually interesting
playing with more hashes, here's a "nice" hash that works on 32 and 64 bit
heh
these are all just an application of the chinese remainder theorem
still fun
Speaking about hash hacks. Here is how to create a ddos attack for dict
!e
def dict_ddos(n):
pow2 = 1
while pow2 < 2 * n: pow2 *= 2
# Fill up all spots that 0 wants to use
A = [pow2]
i = 1
for _ in range(n // 2):
A.append(i)
i = (i * 5 + 1) % pow2
# Spam 0s
A += [0] * (n - len(A))
return A
B = {}
for a in dict_ddos(10**5):
B[a] = 1
@distant salmon :warning: Your 3.11 eval job timed out or ran out of memory.
[No output]
This doesn't make use of hash collisions. Instead what I've done is that I've filled up all the spots that 0 "wants" in the dict. After doing that, checking if 0 is a key in the dict takes O(n) time.
The numbers used here are fairly small, around 2 * n. This is a lot smaller than what you get using multiples of 2^61 - 1.
Btw, in case you are interested, I reduced the code to 1018 bytes now: ```py
A,L,M,J,N,O,P,B,D,E,G,g,k="drei vier fünf sech sieb acht neun zehn elf zwölf sept quin ns ing okt qua mn mx dr non tre en".split()
F,n,o,C,U,m,H,j="ego sanz"
I,S="",10;Q=SS;T,l=QS,J,J,J,H,H,D,I,I
Y=["null","ein","zwei"]+AT
Y[6]+=U;Y[7]+=k
def Z(q,r=I):
a=abs(q);d,u=a//T,2;s=Y[b:=a%Q](a<S)or Z(d%T,"tausend ")+Z(a%T//Q,"hundert")+[Z(a%S,"und")+["zwan",A][b//S-2]+"zß"[b//S==3]+"ig",Y[b](b>0)+A[7](b>12)][b<20]
while d:
V,v=u//2,I
while V:c,e=V//S%S,V//Q%S;h,z=[I,"un","duo",g,P+"ttuor",M,U+F,L+F,O+o,"nove",I,I,I,U,I,I,"x",B,I,B][V%S::S];f,y=[[I,H,"ms",l,"dezi","viginti","tri",P+E+m,M+P,"sexa",L+"ua",O+o,G+m],i:=[I,"nxs",H,l,j,"duz",g+j,P+E+N,M+n,"sesz",L+N,O+N,G+n][e:]][c<1][c::S];v=([H,"mb","tr",P+E,M+"t","sext",L,O,G][V%T:],(h+I.join({z}&{f})+y+"ginta"(c>2)+i[S](c>0)+"enti"(e>0))[:-1])[0]+"illi"+v;V//=T
d//=T;s=Z(p:=d%T,F*(p<2)+C+v.title()+[o+H,"arde"][u%2]+k[u%2:](p>1)+C)+s;u+=1
return[(s:="minus "(q<0)+s+U[a==1!=r>I:b==1])[0].title()+s[1:].strip(),(s+r)*(a>0)][r>I]
zahl=Z
aiming for sub 1k
Thats python code!?
new here?
Very
ah alright
New to programming too
👍
What is this supposed to do?
Have function "zahl" which accepts any positive/negative integer and returns the worded version of the number in German following all the grammar
ah i see
So
zahl(1) == "Eins"
zahl(-10) == "Minus zehn"
zahl(859785271795) == "Achthundertneunundfünfzig Milliarden siebenhundertfünfundachtzig Millionen zweihunderteinundsiebzigtausend siebenhundertfünfundneunzig"
:D
is the strip() even needed?
Yes, I still haven't figured out how to remove the spaces at the end
the "tausend " has a space, unit naming puts a space between each unit, so thinking about special cases actually makes up more bytes than just using str.strip()
Nvm
Welcome to Code Golf!
i did some cursed stuff
but not on such scale
like worst thing i did was this:
pls hold
*A,L,M,J,N,O,P,B,D,E,G,g,k=[*"drei vier fünf sech sieb acht neun zehn elf zwölf sept quin ns ing okt qua mn mx dr non tre en"]
F,n,o,C,U,m,H,j="ego sanz"
I,S="",10;Q=S*S;T,*l=Q*S,J,J,J,H,H,D,I,I
Y=["null","ein","zwei"]+A*T
Y[6]+=U;Y[7]+=k
def Z(q,r=I):
a=abs(q);d,u=a//T,2;s=Y[b:=a%Q]*(a<S)or Z(d%T,"tausend ")+Z(a%T//Q,"hundert")+[Z(a%S,"und")+["zwan",*A][b//S-2]+"zß"[b//S==3]+"ig",Y[b]*(b>0)+A[7]*(b>12)][b<20]
while d:
V,v=u//2,I
while V:c,e=V//S%S,V//Q%S;h,z=[I,"un","duo",g,P+"ttuor",M,U+F,L+F,O+o,"nove",I,I,I,U,I,I,"x",B,I,B][V%S::S];f,y=[[I,H,"ms",*l,"dezi","viginti","tri",P+E+m,M+P,"sexa",L+"ua",O+o,G+m],i:=[I,"nxs",H,*l,j,"duz",g+j,P+E+N,M+n,"sesz",L+N,O+N,G+n][e:]][c<1][c::S];v=(*[H,*"mb","tr",P+E,M+"t","sext",L,O,G][V%T:],(h+I.join({*z}&{*f})+y+"ginta"*(c>2)+i[S]*(c>0)+"enti"*(e>0))[:-1])[0]+"illi"+v;V//=T
d//=T;s=Z(p:=d%T,F*(p<2)+C+v.title()+[o+H,"arde"][u%2]+k[u%2:]*(p>1)+C)+s;u+=1
return[(s:="minus "*(q<0)+s+U[a==1!=r>I:b==1])[0].title()+s[1:].strip(),(s+r)*(a>0)][r>I]
zahl=Z
does this work?
@mortal grotto
Lemme check
print('\n'*int(o:=(b:=int(input("Pyramid base size: ")))%2==1)+'\n'.join((s:=' '*(b//2-i))+'#'*int(o)+'#'*i*2+s for i in range(b//2+1)))
tho i know how to improve it
and yes, i wanted it to be a single statement
Very nice, love those
No it doesn't, the first line doesn't even make sense
it imposes quite some restristions tho
Without strip: 1023 bytes (+5)
We have to check those two places, which I mentioned, and yeah, just str.strip() is shorter in this case
@mortal grotto can u give me some test cases?
numbers with expected responses
nvm ;p
https://gist.github.com/juliusgeo/24a154b9cc4505186f6d1009f4da9b2c you can also ddos lists
that link is broken for me
somehow lost a c at the end
should work now
i'd be curious about how fast dicts fill up compared to lists
Use the working code for test cases
yeah just did that
👍
This is nothing like the thing I was talking about
I was talking about a series of integers which if used as keys for a dict will run super slow
yeah, it's running slow because you allocated a ton of extra data structures due to hash collisions
no extra data or hash collisions
This is only a slow down in time due to dict being fragile
and why is dict fragile? because when the keys collide it requires extra data structures being used
turning something that should be O(1) to O(number of collisions)
not like python data structures, but the underlying dict implementation is allocating extra data structures for every collision
There is absolutely no extra data at all
I didn't say data, I said data structures
you said allocating, there is 0 allocation going on
there absolutely is allocation happening it is just hidden from you
this obviously depends on the exact implementation of the dictionary but hash collisions always cause a degradation in performance because it means you have to figure out which of the collided keys has the value you're looking for
in most implementations this is done by allocating a linked list for that entry in the hash table, and then searching that linked list
Here is a slightly simplified explanation of how dicts in Python work:
Dicts internally store everything in a power of 2 sized list (at least twice the size of the numbers of elements stored in the dict). If you use key = x then it first checks at index x. If that is occupied by something else than x then it checks index 5 * x + 1, and if that is occupied it checks index 5 * (5 * x + 1) + 1...
yeah and if they're all occupied it has to allocate new lists
They are never all occupied
yeah because python allocates new data structures to hold them
The thing that can happen is that it takes ages to find the spot where x is stored
You are talking about "python allocates new data structures". I dont get what you mean by that.
either you turn each index into a linked list, or you allocate a new list to hold the overflow, either way you have to grow the memory size of the dict
The Python interpreter has to allocate new C data structures to hold this stuff
There is no new list to hold the overflow
yeah you don't get an error telling you this the interpreter does it automatically
because it's the expected behavior for dictionaries that they will overflow at some point
there are Python lists (which are I think what you're referring to), but I was referring to the underlying data structures that are implemented in C
the reason you don't see these allocations is because Python's garbage collector etc handle this for you
There cannot be any "overflow", at some point the walk will always find an empty space. But it can take O(n) time
it will always find an empty space because Python automatically allocates new space for it once you exhaust the existing space
!e
def dict_ddos_0(n):
pow2 = 1
while pow2 < 2 * n: pow2 *= 2
# Fill up all spots that 0 wants to use
A = [pow2]
i = 1
for _ in range(n - 1):
A.append(i)
i = (i * 5 + 1) % pow2
return A
# This part runs fast
B = {}
for a in dict_ddos_0(10**5):
B[a] = 1
# Make Python check for 0 in the dict which will take ages since all spots 0
# since the first 10^5 spaces where 0 could be at is occupied
for _ in range(10**5):
0 in B
@distant salmon :warning: Your 3.11 eval job timed out or ran out of memory.
[No output]
See here, absolutely no allocation is going on at all
The expensive part is just checking if 0 is in the dict
This is not a memory ddos, it is purely a time ddos
oh then you should write some code that actually times it
because right now it looks like you're just confirming the fact that, yes, when you have hash collisions it does take longer
that is part of the implementation notes though
"But catering to unusual cases should not slow the usual ones, so we just take
the last i bits anyway. It's up to collision resolution to do the rest. If
we usually find the key we're looking for on the first try (and, it turns
out, we usually do -- the table load factor is kept under 2/3, so the odds
are solidly in our favor), then it makes best sense to keep the initial index
computation dirt cheap."
This is in the comments on the C file I sent earlier
I'm sure that the people that made dict knows about this "attack"
But I'm not so sure most Python users know about it
Well, this is the esoteric python channel which generally involves deep knowledge of CPython's implementation
But the asymptotically bad performance once hash collisions are induced is one of the main drawbacks of most dictionary implementation
It is not that simple.
For example I think that using strings as keys for a dictionary is safe cause their hash is randomized
"Major subtleties ahead: Most hash schemes depend on having a "good" hash
function, in the sense of simulating randomness. Python doesn't: its most
important hash functions (for strings and ints) are very regular in common cases"
Python's hashes are not randomized
From the same C file I sent above
Strings are
and I really wish ints hashes were randomized too
....
did you read the whole quote
because it specifically mentions strings by name
and says they are not random
I'm fairly certain that each time you start up Python it rerandomized the hashes of strings.
!e
print(hash('banana'))
@distant salmon :white_check_mark: Your 3.11 eval job has completed with return code 0.
-7126866263746545998
!e
print(hash('banana'))
@distant salmon :white_check_mark: Your 3.11 eval job has completed with return code 0.
-2654407998515714908
yeah when you restart the interpreter it obviously changes a lot of stuff
arent hashes supposed to be same/consistent?
In Python2 they were
and then people had issues getting with ddosed with string keys
So in Python3 they changed it
So now each time you start Python it rerandomizes it
but how
i just dont really understand this code
even worse
This is the source code for dict
When you try to look up if some key x is in a dict
yes
i thought it would be a bit more complex
I thought so too once, but no, this is it
hm...
The Python3 change appears to just be making the hash inconsistent across interpreter restarts, but in that case you would also lose the entire hash table
As long as the hashes are consistent during the running of the interpreter then the hash table works fine
if you want it to persist you would have to use a different hash implementation
like md5
the existence of a hash seed that changes hash values across program executions does not mean in any way that the hash is now somehow internally random
Just to try to explain how it works. The internal storage is a power of 2 sized list. Then it walks around (using f(x) = 5 * x + 1) until it either finds x or finds an empty spot.
the walking part is to handle collisions?
Ye the internal list is just a rather small power of 2, so it could be (and will likely happen) that the spot you wanted to use is occupied by something else.
So you likely have to walk around for a bit until you either find the spot where x is, or an empty spot (meaning x is not in the dict)
gotcha
and the code you sent before made python to walk the entirity of the thing?
Ye I first occupied the first 10^5 steps of the walk that 0 has
and then I repeatedly asked if 0 is in the dict
10⁵ is just 100 000
it sounds way less in a non-exponential form lol
100 000 is not that big actually
(for modern PCs)
The code does the 100 000 check 100 000 times
That is fairly slow
so it is 10¹⁰
can you add timing code so we can actually see how much slower
Btw, use yield
ye that is how many operations python has to do
It is a crazy many times slower. Like really really slow
yeah, but you need to have actual numbers lol
def dict_ddos_0(n):
pow2 = 1
while pow2 < 2 * n: pow2 *= 2
# Fill up all spots that 0 wants to use
A = [pow2]
i = 1
for _ in range(n - 1):
yield i
i = (i * 5 + 1) % pow2
# This part runs fast
B = {}
for a in dict_ddos_0(10**5):
B[a] = 1
# Make Python check for 0 in the dict which will take ages since all spots 0
# since the first 10^5 spaces where 0 could be at is occupied
for _ in range(10**5):
0 in B
numbers get big stupidly fast
gotta be one of my fav things about numbers
10¹⁵
e15
10²⁰
that code has a bug in it, you want to yield pow2, and not store it inide a list
The pow2 takes the 0 index spot, so it is very important that it is yielded
linux user?
Yeah, didn't look into it too much, but I see now
idk how you type superscript numbers
i just use compose key (linux thingy)
I dont think that the exact numbers matters. The important thing is that it is O(n^2)
Phone
so ³ is COMPOSE + ^ + 3
Google keyboard
how do you know that it is that if you don't have timing numbers
but technically linux user >:)
(android is based on linux, afterall)
it's linux all the way down....
sad arch user
yes
That's why I'm coding on my phone, lol
sorry I was making a reference haha
Time complexity is a wonderful thing. You dont need actual numbers to realize that it is slow.
"Turtles all the way down" is an expression of the problem of infinite regress. The saying alludes to the mythological idea of a World Turtle that supports a flat Earth on its back. It suggests that this turtle rests on the back of an even larger turtle, which itself is part of a column of increasingly larger turtles that continues indefinitely....
But if you really wanna know. On my computer 10^5 case takes 20 s, and 10^6 case takes 2000s
I don't even know what to say to this
There is also a book by John Green with the same name
yup
you have to time the insertion and the lookup separately to be able to compare them
# info simulation
kernel: linux-god-6.9-nice
size: 893825989235 ZiB
speed: 299792458 TPS
particles: ~10⁸² atoms
uptime: 26.7 billion years
you need to make a graph
No I dont have to time the insertion. That part is instant. Only the lookup of 0 is costly
how do you know it is instant if you haven't timed it all out
there are like easily 10 different ways the setup of the code and what is cached etc could influence this
He speaks about it theoretically
I mean that would work if this was a low level language but Python is really not a good language for eyeballing time complexity
yeah I mean theoretically this is the behavior you would expect from every single hash table impl
It is called time complexity. You really should look it up
???
this is graph of O(n²) complexity
as you can see it grows BIG and FAST
not factorial level
O(0)??
yeah I'm aware of what it looks like
but really still bad
I'm saying that for this code, I haven't seen a graph that looks like that
no, O(1)
surely not instant
did you mean "constant"?
Insertion is O(1), lookup of 0 is O(n)
I took way too many data structures classes in undergrad to assume that you can estimate time complexity just by reading real world code
in big o notation instant is O(1)
that would be constant
I said instant cause the all of the insertions take O(n) time, while all of the lookups take O(n^2) time
you dont have to yell :<
the correct nomenclature is constant time, which is ironic because you would know that if you looked up time complexity
I also know from past experience that creating hashtables of size 10^5 takes almost no time at all (significantly less time than 1 s)
tho nothing beats O(n!)
There is always something that beats something else
yeah that's why I'm begging for someone to post an actual graph of the code in question
hahaha
i mean, O(n↑↑↑3) is a thing in theory
i remember 3↑↑↑3 is EXTERMELY large
so it is like
O(n↑ⁿn)
knuth up arrow notation!
love it
I'm sorry but you are getting on my nerves. I dont know what to tell you
I was pretty clear that all I am looking for is timing code and a graph
imagine O(n↑↑↑(n^n)) ☠️
If you can't make the graph, that's fine I can do it for you
Then just make it yourself
I wasn't the one who posted the code....
qalculate doesnt understand them :(
Isn't it your job to justify your own work?
My justification was that !e timed out. As simple as that
3↑3 is (((3^3)^3)^3)
3↑↑3 is (((3↑3)↑3)↑3)
3↑↑↑3 is (((3↑↑3)↑↑3)↑↑3)
and so on
omg
↑ⁿ is equivalent to writing n arrows
that is not a rigorous explanation, especially considering how easy it is in python to measure this the correct way
so what does the arrow mean?
but you're right, give me like 15 minutes and I'll show you a graph of your code
i thoguht it was like higher order of power
and then we can talk about what that graph looks like
like power is the higher order of multiplication
the amount of arrows is the amount of iterations of the previous amount of operations
like multiplication is higher order of addition
In mathematics, Knuth's up-arrow notation is a method of notation for very large integers, introduced by Donald Knuth in 1976.In his 1947 paper, R. L. Goodstein introduced the specific sequence of operations that are now called hyperoperations. Goodstein also suggested the Greek names tetration, pentation, etc., for the extended operations beyon...
it is worse than factorial
imagine actually creating an algo with such big o
thats only with 2 arrows also
yeah ._.
i kinda want to write my custom biginteger and bigfloat implementations (as well as bigfixpoint)
for what language?
rust :)
ah
like, to be able to see
is it even possible to store such large number
actually
if the number is larger than this
it shouldnt be possible
as, it literally doesnt fit
they have nice lil charts for n arrows https://en.wikipedia.org/wiki/Knuth's_up-arrow_notation#Computing_2↑n_b
In mathematics, Knuth's up-arrow notation is a method of notation for very large integers, introduced by Donald Knuth in 1976.In his 1947 paper, R. L. Goodstein introduced the specific sequence of operations that are now called hyperoperations. Goodstein also suggested the Greek names tetration, pentation, etc., for the extended operations beyon...
(at least for 64-bit machines)
it reminds me of multidimensional stuff
yeah
like, 6D or 7D stuff
a cude inside of a cude inside of a cude inside of a cude inside of a cude inside of a cude inside of a cude inside of a cube.
Stumbled upon Graham's number, when reading that Wiki
yeah i was prob referring to that number?
is it it 3↑↑↑3?
G_64, where G_1 = 3^^^3 and G_n = 3^(^G_(n-1)) 3
I'm not sure, give me one second
timing code: ```python
def dict_ddos_0(n):
pow2 = 1
while pow2 < 2 * n: pow2 *= 2
# Fill up all spots that 0 wants to use
A = [pow2]
i = 1
for _ in range(n - 1):
yield i
i = (i * 5 + 1) % pow2
def insert(size):
d = dict_ddos_0(size)
# This part runs fast
B = {}
for a in d:
B[a] = 1
return d
Make Python check for 0 in the dict which will take ages since all spots 0
since the first 10^5 spaces where 0 could be at is occupied
def check(d):
for _ in range(10**5):
0 in d
from timeit import timeit
import matplotlib.pyplot as plt
times = []
sizes = [i for i in range(0, 105, 102)]
bisect_left
num_trials = 5
for size in sizes:
d = insert(size)
times.append(timeit(lambda:check(d), number=num_trials))
plt.plot(sizes, times, label="time spent checking")
plt.xlabel('Input Size')
plt.ylabel('Execution Time (s)')
plt.title('Performance vs Input Size')
plt.legend()
plt.show()
wrong code
You used my version with the bug, lol
yep
oh wait I thought ur version fixed the bug
oh and then there's also https://en.wikipedia.org/wiki/Conway_chained_arrow_notation
Conway chained arrow notation, created by mathematician John Horton Conway, is a means of expressing certain extremely large numbers. It is simply a finite sequence of positive integers separated by rightward arrows, e.g.
2
→
3
→
4
→
5
→
6
{\d...
No, my version introduced the bug :D
ok, running it again with the older code
You should A=[pow2] -> yield pow2
wait that's what my original one did
OH
Ok, I tweaked the timing code and fixed another bug.
idk about y'all, but that does not look like an exponential curve.
You should use the same size for checking 0 for when inserting
Yeah, that was the extra bug I fixed
def dict_ddos_0(n):
pow2 = 1
while pow2 < 2 * n: pow2 *= 2
# Fill up all spots that 0 wants to use
yield pow2
i = 1
for _ in range(n - 1):
yield i
i = (i * 5 + 1) % pow2
def insert(size):
d = dict_ddos_0(size)
# This part runs fast
B = {}
for a in d:
B[a] = 1
return d
# Make Python check for 0 in the dict which will take ages since all spots 0
# since the first 10^5 spaces where 0 could be at is occupied
def check(d, size):
for _ in range(size):
0 in d
from timeit import timeit
import matplotlib.pyplot as plt
times = []
sizes = [i for i in range(0, 10**10, 10**10//50)]
# bisect_left
num_trials = 3
for size in sizes:
d = insert(size)
times.append(timeit(lambda:check(d, size), number=num_trials))
plt.plot(sizes, times, label="time spent checking")
plt.xlabel('Input Size')
plt.ylabel('Execution Time (s)')
plt.title('Performance vs Input Size')
plt.legend()
plt.show()
Oh, wait yeah because it's a generator it isn't passing in d correctly to the check function
def dict_ddos_0(n):
pow2 = 1
while pow2 < 2 * n: pow2 *= 2
# Fill up all spots that 0 wants to use
yield pow2
i = 1
for _ in range(n - 1):
yield i
i = (i * 5 + 1) % pow2
def insert(size):
# This part runs fast
d = {}
for a in dict_ddos_0(size):
d[a] = 1
return d
# Make Python check for 0 in the dict which will take ages since all spots 0
# since the first 10^5 spaces where 0 could be at is occupied
def check(d, size):
for _ in range(size):
0 in d
from timeit import timeit
import matplotlib.pyplot as plt
times = []
sizes = [i for i in range(0, 10**10, 10**10//50)]
# bisect_left
num_trials = 3
for size in sizes:
d = insert(size)
times.append(timeit(lambda:check(d, size), number=num_trials))
plt.plot(sizes, times, label="time spent checking")
plt.xlabel('Input Size')
plt.ylabel('Execution Time (s)')
plt.title('Performance vs Input Size')
plt.legend()
plt.show()
Looks like exp to me
I only tried to 10**5
4*
Yeah I can't try it over 10e5 because it consumes way too much memory
But 10e5 is relatively low for estimating asymptotic behavior
At least now we have actual graphs to compare, makes it a whole lot easier
The fastest algorithm is one which hasn't been profiled yet lol
You can print(size) to see for yourself, that it takes ages
yeah but does it take ages**2 hahahah
that's the question
Looks like that tho
the size is increasing exponentially as well
That is what you would expect
Now, back to my actual goal, when coming here.
Do you think I can merge the two while loops into a single one?
You should be able to do that with walrus operators
Would I be able tho? There are multiple expressions in there
You can combine all the expressions into a single tuple, and then index it to get the values you want to be the conditions
I'm getting a syntax error trying to run your base code though
Really?
It's py 3.11 btw, but should run in 3.6+ probably
doesn't use any super new features
walrus operator requires 3.8+ I believe, but yeah
Yes, 3.8 then
Actually the syntax error I was getting was related to your usage of the walrus operator inside an indexing expression
which isn't allowed IIRC
Did you make any changes since you last posted it? I'm on Python 3.9
!e
Y = [1, 2, 3, 4, 5]
a = 0
Q = 2
Y[b:=a%Q]
@keen thicket :warning: Your 3.11 eval job has completed with return code 0.
[No output]
oh weird....
this is what I get locally with 3.9: File "/Users/juliuspark/one-offs/test.py", line 4 Y[b:=a%Q] ^ SyntaxError: invalid syntax
lemme try on my raspi
Try this ☝️ on your local
yeah this is what I was running locally
looks like it was a feature added in 3.11
Oh, wow
yeah locally it works fine with 3.11
that's cool though I didn't know that was possible
New to me, because I got into golfing, when 3.11 was already out
I'm coding a 1v1 playground for who solves a python exercise the fastest. Who's up for the challenge ??
I am actually running a contest for obfuscated Python code that this would be perfect for
huh??
if it's mobile friendly
I made like one post in this channel a while ago with the mods approval but I haven't been doing a good job of promoting it
Why not
I'm coding it on vue js
is it already running tho?
not yet
I have the exercise generator
using leetcode API
ah
You did this ?
with a lot of help, but yes
Idea for some cursed stuff: self.__dict__ = globals()
Doing globals().clear() in console somehow unpacks the __builtins__ module, lol
Wdym?
>>> globals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>}
>>> globals().clear()
>>> globals()
{'__builtins__': {'__name__': 'builtins', '__doc__':
...
Oh. I did not know, you can actually remove global things. ran __builtins__.clear() and now print() is not accessible
Because __builtins__ can contain two things: module itself or its dict
When interpreter finds global dict with no ref to builtins, it replaces it with builtins dict
This will definitely break everything, because it clears builtin namespace for all modules, not only for your local scope
Sounds like fun
And it can easily be reset by just quitting the console
command line
I forgot, how do you call the "command line interpreter"?
Repl
Ah, yes
read-eval-print loop
!e from #1137799337838116974 ```py
d = {'a': 1, 'b': 2, 'c': 0}
print(min(d, key=d.get))
@fleet bridge :white_check_mark: Your 3.11 eval job has completed with return code 0.
c
oh i love that
that's quite nice
Btw here is a fun thing that you can do in Python
!e
A = [0,0,0]
i = A[i] = 2
print(A)
@distant salmon :white_check_mark: Your 3.11 eval job has completed with return code 0.
[0, 0, 2]
I have used this for codegolf in the past
!e ```py
a = a[0] =[...]
print(a)
@fleet bridge :white_check_mark: Your 3.11 eval job has completed with return code 0.
[[...]]
Here is another weird thing many people probably dont know about
Most people know that you can do
!e
A = [1,2,3]
B = [4,5,6]
A += B
@distant salmon :warning: Your 3.11 eval job has completed with return code 0.
[No output]
But did you know that this doesn't work
!e
A = [1,2,3]
B = [4,5,6]
def f():
A += B
f()
@distant salmon :x: Your 3.11 eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "/home/main.py", line 7, in <module>
003 | f()
004 | File "/home/main.py", line 5, in f
005 | A += B
006 | ^
007 | UnboundLocalError: cannot access local variable 'A' where it is not associated with a value
This is the reason that I prefer to use .extend over +=
