Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Locked thread
Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

Hammerite posted:

Is it considered poor form to use the fact that loop variables are still set after the loop?

I have to repeatedly loop over elements of a dictionary and unset an element at each iteration, but can't unset in the loop because that's not allowed. So I came up with

Python code:
while len(myDict):
    for k in myDict:
        if condition(myDict[k]):
            do_things()
            break
    else:
        raise MyCustomException('badly formed dict!')
    del myDict[k]
Here I use the fact that k is still set after the loop is broken out of.

I'm not sure about that exact situation, but it's not utterly uncommon for me to break out of a loop and use the last set value of the loop. What's happening isn't completely explicit, either, so I don't do it all the time.

Adbot
ADBOT LOVES YOU

Lichtenstein
May 31, 2012

It'll make sense, eventually.
During my learning pursuits I got a brilliant idea of splitting part of rarely-used code (namely, the random level generator) into a module, partially to make browsing the code more convenient and partially just to learn. It resulted in a horrible clusterfuck of shamefully bad code. Before I try to tackle it again, there are some things that confused me (probably mainly due to my lack of experience with coding at all):

1) Do I have this right: to read a variable from another module I have to simply import it and slap moduleName. before it. To actually change the imported variable I have to slap "global" on it in the source module?

2) If I do the "from myModule import *", everything from the first point still applies to variables, except I'm spared the effort of writing moduleName. before them? What would happen if I did it while loving up and having a variable that's named exactly the same in each file?

3) How do classes and their instances work with modules? If I define a class and its particular instance, what do I need to set up to read one of that instance's attributes in another module?

4) Do I have this right: unless there's some magic involving the aforementioned instances, the only sane/proper way of sending some data to module and getting back some processed data without delving into some horrible circular import hell is to put everything we want into the return statement of the imported function. Dumping the results into a .txt file doesn't count.

Plorkyeran
Mar 22, 2007

To Escape The Shackles Of The Old Forums, We Must Reject The Tribal Negativity He Endorsed

Lichtenstein posted:

1) Do I have this right: to read a variable from another module I have to simply import it and slap moduleName. before it. To actually change the imported variable I have to slap "global" on it in the source module?
Don't do this.

Lichtenstein posted:

2) If I do the "from myModule import *", everything from the first point still applies to variables, except I'm spared the effort of writing moduleName. before them? What would happen if I did it while loving up and having a variable that's named exactly the same in each file?
Don't do this.

Lichtenstein posted:

3) How do classes and their instances work with modules? If I define a class and its particular instance, what do I need to set up to read one of that instance's attributes in another module?
Nothing.

Lichtenstein posted:

4) Do I have this right: unless there's some magic involving the aforementioned instances, the only sane/proper way of sending some data to module and getting back some processed data without delving into some horrible circular import hell is to put everything we want into the return statement of the imported function. Dumping the results into a .txt file doesn't count.
Functions are a thing. Use them.

duck monster
Dec 15, 2004

QuarkJets posted:

If by "pretty" you mean "readable" then shouldn't that be part of "make it work?" Most scientific programming is done in the style of "I'm going to get this to work, I don't care if it's fast or readable", and it's actually a huge problem when a change to the code needs to be made but the entire house of cards falls apart because the code has turned into a black box and no one knows what makes it work

It can also have political implications. The climate gate frame up partly revolved around a series of inexperienced coders with no experience in dealing with scientist written bad code declaring it MUST be dodgy because it was so "obfuscated". No kids, that's not obfuscated, it's just garbage quality fortran and entirely functional and works too.

Also, don't call your model calibrations "fudge factors". Loons will skewer you for it.

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

duck monster posted:

series of inexperienced coders

I think I recall Eric Raymond being amongst them. While not inexperienced, he has his own set of issues.

fart simpson
Jul 2, 2005

DEATH TO AMERICA
:xickos:

Lichtenstein posted:

4) Do I have this right: unless there's some magic involving the aforementioned instances, the only sane/proper way of sending some data to module and getting back some processed data without delving into some horrible circular import hell is to put everything we want into the return statement of the imported function. Dumping the results into a .txt file doesn't count.

What does this even mean?

QuarkJets
Sep 8, 2008

Question for the thread: what if I have a module defined like this:

Python code:
#myModule.py

const1 = "test"
const2 = "whatever"

class class1:
  #some class

def function1:
  #some function
Is it considered poor form to keep variables (which I'm treating as constants) in the base of a module like this? This seems useful as it allows me to import the constants in other modules (from myModule import const1), and if I ever change those constants in the future then I only have to change a single line in a single module

Lichtenstein posted:

During my learning pursuits I got a brilliant idea of splitting part of rarely-used code (namely, the random level generator) into a module, partially to make browsing the code more convenient and partially just to learn. It resulted in a horrible clusterfuck of shamefully bad code. Before I try to tackle it again, there are some things that confused me (probably mainly due to my lack of experience with coding at all):

1) Do I have this right: to read a variable from another module I have to simply import it and slap moduleName. before it. To actually change the imported variable I have to slap "global" on it in the source module?

2) If I do the "from myModule import *", everything from the first point still applies to variables, except I'm spared the effort of writing moduleName. before them? What would happen if I did it while loving up and having a variable that's named exactly the same in each file?

3) How do classes and their instances work with modules? If I define a class and its particular instance, what do I need to set up to read one of that instance's attributes in another module?

4) Do I have this right: unless there's some magic involving the aforementioned instances, the only sane/proper way of sending some data to module and getting back some processed data without delving into some horrible circular import hell is to put everything we want into the return statement of the imported function. Dumping the results into a .txt file doesn't count.

Your module should consist of classes and/or functions that are used in randomly generating a level. Maybe some other module somewhere creates an instance of one of the classes (in which case it has "from mymodule import classname in it", or maybe it calls one of the functions, but you certainly don't need to be using global variables or polluting your namespace with "from module import *"

For your last question, you could send some data to a class or function within the module, and then that class or function could hold or return the processed data. No circular import hell, no hard drive access, just simple calls.

From your questions, it sounds like you are basically writing your modules as scripts and then trying to get them to call each other. Don't do that. Put all of the code in your module into classes and functions within that module, and then import those classes and/or functions when they're needed elsewhere.

QuarkJets fucked around with this message at 05:46 on May 15, 2013

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe

Lichtenstein posted:

2) If I do the "from myModule import *", everything from the first point still applies to variables, except I'm spared the effort of writing moduleName. before them? What would happen if I did it while loving up and having a variable that's named exactly the same in each file?

You would clobber the existing variable in the module where the import statement is.

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe

Dren posted:

This seems like a job for a heapsort.

I'm not sorting it. Here, it will be more straightforward if I just post my actual code.

http://pastebin.com/ikaisXAA

The relevant function is getPatternDict().

Lichtenstein
May 31, 2012

It'll make sense, eventually.
Thanks a lot, it's much more understandable now and I managed to make it work.

JetsGuy
Sep 17, 2003

science + hockey
=
LASER SKATES

Lichtenstein posted:

4) Do I have this right: unless there's some magic involving the aforementioned instances, the only sane/proper way of sending some data to module and getting back some processed data without delving into some horrible circular import hell is to put everything we want into the return statement of the imported function. Dumping the results into a .txt file doesn't count.

Are you asking how do you get data returned from your module? Because it's pretty much going to return data in the same way it would if it was in your code to begin with (with the additional module_name. before your call).

Haystack
Jan 23, 2005





QuarkJets posted:

Is it considered poor form to keep variables (which I'm treating as constants) in the base of a module like this? This seems useful as it allows me to import the constants in other modules (from myModule import const1), and if I ever change those constants in the future then I only have to change a single line in a single module

That sounds completely fine, so long as the constants are all fairly independent. Big groups of constants (such as, say, application settings) are better off being grouped together in a dict or namedtuple or something.

Vulture Culture
Jul 14, 2003

I was never enjoying it. I only eat it for the nutrients.

Hammerite posted:

At each step in the process, at least one of the elements of the dictionary should satisfy the if clause (otherwise the dictionary shall be considered badly-formed by definition). However, I don't know which one(s).
This has a serious code smell. Can you go into a little more detail about what's actually going on here, how that dict gets populated, etc.?

Dren
Jan 5, 2001

Pillbug

Hammerite posted:

I'm not sorting it. Here, it will be more straightforward if I just post my actual code.

http://pastebin.com/ikaisXAA

The relevant function is getPatternDict().

I think it's a little strange that you transform the dictionary in place but if you're after efficiency I suppose it makes sense. Then again, if you're after efficiency you probably ought to write it all in C.

Any particular reason you have these pre-schema files instead of plain-ol' json schema?

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe

Dren posted:

I think it's a little strange that you transform the dictionary in place but if you're after efficiency I suppose it makes sense. Then again, if you're after efficiency you probably ought to write it all in C.

Any particular reason you have these pre-schema files instead of plain-ol' json schema?

The choice to transform the dictionary in place was not based on anything in particular.

The main motivation is the following note from http://json-schema.org/latest/json-schema-validation.html#anchor6

quote:

Furthermore, given the high disparity in regular expression constructs support, schema authors SHOULD limit themselves to the following regular expression tokens:
  • individual Unicode characters, as defined by the JSON specification [RFC4627];
  • simple character classes ([abc]), range character classes ([a-z]);
  • complemented character classes ([^abc], [^a-z]);
  • simple quantifiers: "+" (one or more), "*" (zero or more), "?" (zero or one), and their lazy versions ("+?", "*?", "??");
  • range quantifiers: "{x}" (exactly x occurrences), "{x,y}" (at least x, at most y, occurrences), {x,} (x occurrences or more), and their lazy versions;
  • the beginning-of-input ("^") and end-of-input ("$") anchors;
  • simple grouping ("(...)") and alternation ("|").

I wanted to write regular expressions using named subexpressions and without significant whitespace. The algorithm that prompted me to ask the question is related to the construction of a dictionary of named subexpressions which might initially refer to one another. These references first need to be resolved so that each subexpression stands on its own. It could happen that some subexpression references by name another subexpression that does not in fact exist, or that a circular relationship exists between subexpressions. Hopefully it is obvious that collections of subexpressions like this are no good. The whitespace thing is permitted in a primitive way by allowing the pattern property to be a list, which is ''.join()'d.

I took the opportunity to add a few bells and whistles, like the ability to write (for example) "_INTEGER": [4, 8] and have it be interpreted as "type": "integer", "minimum": 4, "maximum": 8.

A regular expression in a preschema:

code:
"_TRANSFORMPATTERN": [
   "^(",
      "<abstractPointIdentifier>|",
      "<locationidentifier>:(",
         "[1-4]:(<intercardinalOrMiddle>|<cardinal>(:<parameter48%/>)?)|",
         "name:(<intercardinalOrMiddle>|<cardinal>(:<parameter300%/>)?)|",
         "<intercardinalOrMiddle>|",
         "<cardinal>(:<parameter196%/>)?",
      ")|",
      "<linkIdentifier>:(",
         "<locationIdentifier>|",
         "start|end|midway|",
         "<intercardinalOrMiddle>|",
         "<cardinal>(:<parameter800%/>)?|",
         "[1-4]:(<locationIdentifier>|start|end|midway|<parameter400%/>)|",
         "marker(:(",
            "<intercardinalOrMiddle>|",
            "<cardinal>(:<parameter18%bad/>)?",
         "))?|",
         "<parameter1500%/>",
      ")|",
      "<widgetIdentifier>:(",
         "<intercardinalOrMiddle>|",
         "<cardinal>(:<parameter500%/>?)",
      ")",
   ")",
   "(\\+\\(",
      "-?<positiveparameter200>,",
      "-?<positiveparameter200>",
   "\\))?$"
]
The regular expression in the processed schema (with line breaks added for tables):

code:
"pattern":
"^((\\.[a-zA-Z0-9]{3,12})|(@[a-zA-Z0-9]{3,12}):([1-4]:(((northeast|ne|southeast
|se|southwest|sw|northwest|nw)|middle|m)|(north|n|east|e|south|s|west|w)(:(-?([
1-3]?[0-9]?|4[0-8])|(-?([1-9]?[0-9]?|100)%)|(-?(1/2|[12]/3|[123]/4|[1-5]/6|[1-7
]/8|([1-9]|10|11)/12))))?)|name:(((northeast|ne|southeast|se|southwest|sw|north
west|nw)|middle|m)|(north|n|east|e|south|s|west|w)(:(-?([1-9]?[0-9]?|[12][0-9]{
2}|300)|(-?([1-9]?[0-9]?|100)%)|(-?(1/2|[12]/3|[123]/4|[1-5]/6|[1-7]/8|([1-9]|1
0|11)/12))))?)|((northeast|ne|southeast|se|southwest|sw|northwest|nw)|middle|m)
|(north|n|east|e|south|s|west|w)(:(-?([1-9]?[0-9]?|1[0-8][0-9]|19[0-6])|(-?([1-
9]?[0-9]?|100)%)|(-?(1/2|[12]/3|[123]/4|[1-5]/6|[1-7]/8|([1-9]|10|11)/12))))?)|
(\\^[a-zA-Z0-9]{3,12}):((@[a-zA-Z0-9]{3,12})|start|end|midway|((northeast|ne|so
utheast|se|southwest|sw|northwest|nw)|middle|m)|(north|n|east|e|south|s|west|w)
(:(-?([1-9]?[0-9]?|[1-7][0-9]{2}|800)|(-?([1-9]?[0-9]?|100)%)|(-?(1/2|[12]/3|[1
23]/4|[1-5]/6|[1-7]/8|([1-9]|10|11)/12))))?|[1-4]:((@[a-zA-Z0-9]{3,12})|start|e
nd|midway|(-?([1-9]?[0-9]?|[123][0-9]{2}|400)|(-?([1-9]?[0-9]?|100)%)|(-?(1/2|[
12]/3|[123]/4|[1-5]/6|[1-7]/8|([1-9]|10|11)/12))))|marker(:(((northeast|ne|sout
heast|se|southwest|sw|northwest|nw)|middle|m)|(north|n|east|e|south|s|west|w)(:
(-?([0-9]|1[0-8])|(-?([1-9]?[0-9]?|100)%)|(-?(1/2|[12]/3|[123]/4|[1-5]/6))))?))
?|(-?([1-9]?[0-9]?|([1-9]|1[0-4])[0-9]{2}|1500)|(-?([1-9]?[0-9]?|100)%)|(-?(1/2
|[12]/3|[123]/4|[1-5]/6|[1-7]/8|([1-9]|10|11)/12))))|(%[a-zA-Z0-9]{3,12}):(((no
rtheast|ne|southeast|se|southwest|sw|northwest|nw)|middle|m)|(north|n|east|e|so
uth|s|west|w)(:(-?([1-9]?[0-9]?|[1-4][0-9]{2}|500)|(-?([1-9]?[0-9]?|100)%)|(-?(
1/2|[12]/3|[123]/4|[1-5]/6|[1-7]/8|([1-9]|10|11)/12)))?)))(\\+\\(-?([1-9]?[0-9]
?|1[0-9]{2}|200),-?([1-9]?[0-9]?|1[0-9]{2}|200)\\))?$"
You can see that this regular expression would be tiresome to write by hand, notwithstanding that its length is inflated slightly by superfluous pairs of brackets.

Hammerite fucked around with this message at 17:31 on May 15, 2013

geera
May 20, 2003
What's the preferred way to parse XML now? I'm seeing lxml, BeautifulSoup, etc being mentioned. I don't need a ton of features, just something that will let me extract values from an XML structure that I provide quickly and easily.

Suspicious Dish
Sep 24, 2011

2020 is the year of linux on the desktop, bro
Fun Shoe
Use lxml. It's good.

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

Suspicious Dish posted:

Use lxml. It's good.

Wrong.

It's great!

I haven't done anything needing lxml-ish abilities in a few years until recently. Last time I did it I was using BS, this time I tried out lxml and am quite pleased.

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe
Followup question to what I was talking about before: what is the correct abstract base class to use for lists (in Python 3)?

I was using Sequence as an abstract base class for lists, but then ran into a bug where my recursing function would go beyond the recursion limit. I eventually worked out it was because Sequence matches strings as well as lists, so I changed it to MutableSequence.

JetsGuy
Sep 17, 2003

science + hockey
=
LASER SKATES
Hey y'all, I understand that PEP8 is a guideline and all, but I was just wondering if there was an accepted way to fix this. I have a feeling this just may be one of those cases where I have to just let it be.

I have lines in a code that is very long, and really can't be cut down too much further without affecting the readability of the var names. E.g.:

Python code:
marker = usr_deflts["markers_list"][key_cnt%len(usr_deflts["marker_list"])]
which is like 75 characters to begin with. This is in a loop such that it's already 12 chars indented, and I'm really just defining marker because it originally was in a function call which would have made the line like 105-127 depending on how I indent the args in the function.

Again, I realize that PEP8 is a style thing, but I was just curious if there was some pythonic trick to avoiding this. I can't really see much of a workaround.

Jewel
May 2, 2009

JetsGuy posted:

Hey y'all, I understand that PEP8 is a guideline and all, but I was just wondering if there was an accepted way to fix this. I have a feeling this just may be one of those cases where I have to just let it be.

I have lines in a code that is very long, and really can't be cut down too much further without affecting the readability of the var names. E.g.:

Python code:
marker = usr_deflts["markers_list"][key_cnt%len(usr_deflts["marker_list"])]
which is like 75 characters to begin with. This is in a loop such that it's already 12 chars indented, and I'm really just defining marker because it originally was in a function call which would have made the line like 105-127 depending on how I indent the args in the function.

Again, I realize that PEP8 is a style thing, but I was just curious if there was some pythonic trick to avoiding this. I can't really see much of a workaround.

Why not just make a function?

Python code:
>>> usr_deflts = {"markers_list":["key1", "key2", "key3"]}
>>> def wrap(arr, n):
	return arr[n%len(arr)]
>>> for i in range(7):
	print wrap(usr_deflts["markers_list"], i)

key1
key2
key3
key1
key2
key3
key1

accipter
Sep 12, 2003

JetsGuy posted:

I have lines in a code that is very long, and really can't be cut down too much further without affecting the readability of the var names. E.g.:

Python code:
marker = usr_deflts["markers_list"][key_cnt%len(usr_deflts["marker_list"])]

This is another potential solution:
Python code:
ml = usr_deflts["markers_list"]
marker = ml[key_cnt % len(ml)]

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe

JetsGuy posted:

Hey y'all, I understand that PEP8 is a guideline and all, but I was just wondering if there was an accepted way to fix this. I have a feeling this just may be one of those cases where I have to just let it be.

I have lines in a code that is very long, and really can't be cut down too much further without affecting the readability of the var names. E.g.:

Python code:
marker = usr_deflts["markers_list"][key_cnt%len(usr_deflts["marker_list"])]
which is like 75 characters to begin with. This is in a loop such that it's already 12 chars indented, and I'm really just defining marker because it originally was in a function call which would have made the line like 105-127 depending on how I indent the args in the function.

Again, I realize that PEP8 is a style thing, but I was just curious if there was some pythonic trick to avoiding this. I can't really see much of a workaround.

In addition to the ideas already posted,

Python code:
marker = (
   usr_deflts ["markers_list"]
              [key_cnt % len(usr_deflts["marker_list"])]
)
Although I would be more put off by the variable name "usr_deflts" personally.

JetsGuy
Sep 17, 2003

science + hockey
=
LASER SKATES
All doable ideas guys, thanks a ton.

fritz
Jul 26, 2003

I got tasked to 'make a webserver to do (this stuff)', but as I'm basically a data scientist on a suddenly short-staffed team, I don't really know what I'm doing.

The thing I came up with is to use cherrypy and multiprocessing, when a POST request comes in, the cherrypy code does some preprocessing and :
code:
pool.apply_async(wrapper, tuple(),opts)
(where
code:
pool = multiprocessing.Pool() 
was declared at the top level of the file)

This works fine, everything's great, except when wrapper() finishes, I'm still seeing heavy cpu and memory usage by looking at ps. For example, I sent off four POST requests (on an eight core machine) and this is what ps looks like, a half hour after the jobs finished:

code:
508      17302  2.7  0.3 648288 121084 pts/0   Sl+  16:55   1:52 python ./miniserv.py
508      17308 49.4 12.6 4373984 3963644 pts/0 S+   16:55  33:31 python ./miniserv.py
508      17309 49.3 12.6 4374392 3964052 pts/0 S+   16:55  33:26 python ./miniserv.py
508      17310 49.7 12.6 4374600 3964256 pts/0 S+   16:55  33:42 python ./miniserv.py
508      17311 49.7 12.6 4375592 3965248 pts/0 S+   16:55  33:41 python ./miniserv.py
508      17312  0.0  0.2 452096 82028 pts/0    S+   16:55   0:00 python ./miniserv.py
508      17313  0.0  0.2 452096 82028 pts/0    S+   16:55   0:00 python ./miniserv.py
508      17314  0.0  0.2 452096 82028 pts/0    S+   16:55   0:00 python ./miniserv.py
508      17315  0.0  0.2 452096 82028 pts/0    S+   16:55   0:00 python ./miniserv.py
I'm presuming the 17302 process is the server itself, and the other 8 are the multiprocessing.Pool processes. CPU usage is slowly decreasing, maybe a percentage point or so per minute. So I guess my questions are:
* what am I doing wrong?
* what's the right way to do this?

EDIT:
a couple hours later, the %CPU is down around 20% but the total CPU time hasn't budged. Is ps giving me unreliable information?

fritz fucked around with this message at 03:46 on May 17, 2013

Dominoes
Sep 20, 2007

PYQT question. I created a keypress event based on code I found in a google search. It works, but I don't know why.

I added this function under my main window's class:
Python code:
def keyPressEvent(self, e):
    if e.key() == PyQt4.QtCore.Qt.Key_Delete:
        Main.delete(self)  
Where Main.delete is the function I want to call when the delete key is pressed.

This function, keyPressEvent, is never called. Why does it work?

Dominoes fucked around with this message at 02:08 on May 18, 2013

accipter
Sep 12, 2003

Dominoes posted:

PYQT question. I created a keypress event based on code I found in a google search. It works, but I don't know why.

I added this function under my main window's class:
Python code:
def keyPressEvent(self, e):
    if e.key() == PyQt4.QtCore.Qt.Key_Delete:
        Main.delete(self)  
Where Main.delete is the function I want to call when the delete key is pressed.

This function, keyPressEvent, is never called. Why does it work?

keyPressEvent() is called whenever you press a key. Read about keyPressEvent here. You might find it interesting to add "print e" to that method and see what happens.

Dominoes
Sep 20, 2007

So it's a special function name. I notice that changing its name makes it not work.

accipter
Sep 12, 2003

Dominoes posted:

So it's a special function name. I notice that changing its name makes it not work.

It is a class method that you are overloading.

Lichtenstein
May 31, 2012

It'll make sense, eventually.
It'll probably turn out to be something simple, but I'm at loss here:

code:
  plik = shelve.open('data\data')
  max_points = plik['max_points'] #int, sums points from all previous games
  threshold = plik['unlock_threshold'] # int, points needed to unlock new stuff
  factor = plik['unlock_factor'] # int, for increasing future thresholds
  unlocks = plik['unlocks'] # list of unlockable things
  items = plik['unlocked_items'] # list of already unlocked things
  max_points += points
  plik.close()
  if len(unlocks) > 0:
    while True:
      if max_points >= threshold:
        if unlocks[0].item: items.append(unlocks[0]) # pointless currently, but I expect more types in future.
        del unlocks[0]
        factor += BASE_FACTOR
        threshold += factor
      elif max_points < threshold: break
  plik = shelve.open('data\data', 'w')
  plik['max_points'] = max_points
  plik['unlock_factor'] = factor
  plik['unlock_threshold'] = threshold
  plik['unlocks'] = unlocks
  plik['unlocked_items'] = items
  plik.close()
(The closing and opening of the file is probably pointless, it's a leftover from my 'try everything' attempts)

What this code is meant to do is to check if player accumulated enough points to unlock a new item, then move the first item in unlocks list to a new place and increase future threshold (in a similar progression of that of D&D). Then check again until there's no more unlocks.

The line "threshold += factor" is all hosed up, though. threshold doesn't increase, ever (I whipped up a second program to look up what's in the file). Yet for the second item in the unlock list the "if max_points >= threshold" seems to work more like "if max_points >= threshold+factor".

It's some sort of "it's not a variable but a reference to one"-style voodoo, isn't it?

QuarkJets
Sep 8, 2008

Lichtenstein posted:

It'll probably turn out to be something simple, but I'm at loss here:

code:
 snip
(The closing and opening of the file is probably pointless, it's a leftover from my 'try everything' attempts)

What this code is meant to do is to check if player accumulated enough points to unlock a new item, then move the first item in unlocks list to a new place and increase future threshold (in a similar progression of that of D&D). Then check again until there's no more unlocks.

The line "threshold += factor" is all hosed up, though. threshold doesn't increase, ever (I whipped up a second program to look up what's in the file). Yet for the second item in the unlock list the "if max_points >= threshold" seems to work more like "if max_points >= threshold+factor".

It's some sort of "it's not a variable but a reference to one"-style voodoo, isn't it?

Does your external code check threshold or plik['unlock_threshold']? And what is BASE_FACTOR set to? Maybe you could explain a little bit more about what this external code checked.

Have you tried just printing threshold before and after you increment it? Can you show us that output?

Also, the closing of the file also calls sync(), which writes back all entries in the cache. So you'd need to either close the file or call sync() after setting all of those dictionary values.

Lichtenstein
May 31, 2012

It'll make sense, eventually.
Right after resetting the file, factor, threshold and BASE_FACTOR are each 5.000.

After hitting 5.000 max_points, threshold is left at the very same 5.000, while factor increases to 10.000. After hitting 10.000 max_points, nothing happens. After hitting 15.000 max_points, second item gets unlocked and factor increases to 15.000.

The only other interactions with the file in external code is a simple random.choice() of the unlocked items to spawn, which works fine.

Met48
Mar 15, 2009

Lichtenstein posted:

Right after resetting the file, factor, threshold and BASE_FACTOR are each 5.000.

After hitting 5.000 max_points, threshold is left at the very same 5.000, while factor increases to 10.000. After hitting 10.000 max_points, nothing happens. After hitting 15.000 max_points, second item gets unlocked and factor increases to 15.000.

The only other interactions with the file in external code is a simple random.choice() of the unlocked items to spawn, which works fine.

What behaviour do you want? It sounds like you want the second unlock to occur at 10,000 points, which you can accomplish by swapping factor += BASE_FACTOR and threshold += factor. Alternatively, you may want to store a level number and then have a function that can generate the threshold for the next level. This would be easier to reason about than tracking the state of both factor and threshold. Another alternative is to just store the point thresholds in the item objects.

I would also double-check your logging; threshold definitely increases when you hit 5,000 points. Here's the version I'm testing with:

Python code:
import shelve

BASE_FACTOR = 5000

def add_points(points):
    shelf = shelve.open('data/data')

    sum_points = shelf.get('max_points', 0)
    threshold = shelf.get('unlock_threshold', BASE_FACTOR)
    factor = shelf.get('unlock_factor', BASE_FACTOR)
    unlockables = shelf.get('unlocks', [])
    items = shelf.get('unlocked_items', [])

    print "Current Points:", sum_points
    print "Current Factor:", factor
    print "Current Threshold:", threshold
    print "Current Items:", items

    sum_points += points

    while unlockables and sum_points >= threshold:
        if unlockables[0].item:
            # Pointless currently, but I expect more types in future.
            items.append(unlockables[0])
        del unlockables[0]
        factor += BASE_FACTOR
        threshold += factor

    print "New Points:", sum_points
    print "New Factor:", factor
    print "New Threshold:", threshold
    print "New Items:", items

    shelf['max_points'] = sum_points
    shelf['unlock_factor'] = factor
    shelf['unlock_threshold'] = threshold
    shelf['unlocks'] = unlockables
    shelf['unlocked_items'] = items

    shelf.close()
And the console output from adding 5,000 a few times:

code:
Current Points: 0
Current Factor: 5000
Current Threshold: 5000
Current Items: []

New Points: 5000
New Factor: 10000
New Threshold: 15000
New Items: [Item(item='one')]

...

New Points: 10000
New Factor: 10000
New Threshold: 15000
New Items: [Item(item='one')]

...

New Points: 15000
New Factor: 15000
New Threshold: 30000
New Items: [Item(item='one'), Item(item='two')]

Met48 fucked around with this message at 19:56 on May 19, 2013

Lichtenstein
May 31, 2012

It'll make sense, eventually.
It turns out everything was fine, the busted part was the code I used to peek on the file. Even though it literally consisted only of opening and closing the file and print() in between. :psyduck:

Out of curiosity, instead of using .get() method like Met48 did I simply wrote print(shelf['whatever']). Why was this (apparently) a bad, retarded rookie mistake?

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

.get() doesn't throw an exception when you try to get a non-existent key.

duck monster
Dec 15, 2004

Thermopyle posted:

I think I recall Eric Raymond being amongst them. While not inexperienced, he has his own set of issues.

batshit insane libertarian would the issue in his case.

Dominoes
Sep 20, 2007

What's the best way to get the current path a file is run from? I've been using abspath('')+ '\\folder', but this relies on the folder name not changing. Is there a way to get the actual path of the file, instead of one level up? (which abspath() seems to do)

Using __file__ with dirname seems to work, but it causes cx_freeze to glitch out.

Dominoes fucked around with this message at 23:49 on May 19, 2013

BeefofAges
Jun 5, 2004

Cry 'Havoc!', and let slip the cows of war.

Thermopyle posted:

.get() doesn't throw an exception when you try to get a non-existent key.

Instead, it just returns None.

BeefofAges
Jun 5, 2004

Cry 'Havoc!', and let slip the cows of war.

Dominoes posted:

What's the best way to get the current path a file is run from? I've been using abspath('')+ '\\folder', but this relies on the folder name not changing. Is there a way to get the actual path of the file, instead of one level up? (which abspath() seems to do)

Using __file__ with dirname seems to work, but it causes cx_freeze to glitch out.

os.getcwd()?

Adbot
ADBOT LOVES YOU

Dominoes
Sep 20, 2007

BeefofAges posted:

os.getcwd()?
I was using that until I realized it only works when running the script from its directory, ie through the editor directly. It returns my home folder when running through a command prompt or by clicking the file.

edit: Looks like abspath()'s doing the same thing cwd did. Oops - doublebroke.

Perhaps dirname(__file__) is the answer, and I just need to troubleshoot the cx_freeze error.

Dominoes fucked around with this message at 00:14 on May 20, 2013

  • Locked thread