Redleaf

Members
  • Content count

    266
  • Joined

  • Last visited

Community Reputation

219 Neutral

About Redleaf

  • Rank
    Member

Personal Information

  1. Extending python with SWIG

    Have you taken a look at Pyrex yet? I'm sharing this in case you're doing some exploring to find alternative ways to create Python extensions. I've used SWIG too, and Pyrex seems a lot nicer to work with.
  2. Writing Files to a Remote Client in Python

    Good question. I forget the exact path I took while learning to use twisted. I would begin by reading some of the core developer how-to guides. I don't recommend reading them all, or even from top to bottom, as a lot of that information probably won't be important to you. I think I started with Perspective Broker, which will probably be the kind of network programming you're looking for.
  3. Writing Files to a Remote Client in Python

    Straying from your technical question slightly, if you really want to see python's power when it comes to network programming, maybe you should give twisted-matrix a try. Your file-sharing app might be much easier to write this way.
  4. Well, the problem is that __del__ will only be called when the toto object is being cleaned up. Therefore, a del self.Gen statement will also only be executed then. However, as long as there are references to the toto object, it will not be cleaned up. self.Gen has one of those references, and so it creates a reference cycle with the toto object.
  5. Because of the way a proxy works, when you call a method through one, it actually will be the object that it is a proxy to. These two will be equivalent: self.Gen = proxy(self).Generator() self.Gen = self.Generator() So this way will not work. However, there is a way to do what you want. self.Gen = toto.Generator(proxy(self)) By calling the method through the class and passing the proxy explicitly, the self parameter is now actually the proxy. In case you ever decide you need to be more general about the class, you can do something like this: self.Gen = self.__class__.Generator(proxy(self)) This might be an even better solution. Hope this helps.
  6. Sorry, there is a slight problem with the example I gave. You have to make sure you iterate your generator at least once in order to get the proxy installed. If the generator is never iterated, it will continue to hold the original self and prevent cleanup. Having it install during the object creation is relatively simple to do though: from weakref import proxy class toto: def __init__(self): print "Init" self.Gen = self.Generator() self.Gen.next() # install the proxy def __del__(self): print "Del" def Generator(self): self = proxy(self) # now there won't be zombification yield None # end of proxy installation for i in range (100): print i yield i def Step(self): self.Gen.next() Now your objects can be cleaned up even if they never call Step().
  7. Good question, and I'm glad you asked. This is actually a reasonably simple problem to solve with weakrefs. You can read more about the module "weakref" in the python docs. Lots of good information. Anyway, a simple proxy will prevent your generator from storing a reference to the instance, and it can then be cleaned up as you expect. from weakref import proxy class toto: def __init__(self): print "Init" self.Gen = self.Generator() def __del__(self): print "Del" def Generator(self): self = proxy(self) # now there won't be zombification # note that you only need self if you make use of it here for i in range (100): print i yield i def Step(self): self.Gen.next() Of course, as in your example, if you don't actually make use of the "self" object, so you can simply del self, or not even have the generator as a method.
  8. Python: for loops

    What he meant was that if you had an original list with multiple references: data = [1,2,3,4,5] data2 = data Then this line would cause changes in data to also be seen by data2: data[:] = list(line.split() for line in data)
  9. Python: for loops

    All of that makes sense. Thank you both for the clarification.
  10. Python: for loops

    Departing slightly from the main topic, how would using a generator expression compare? data = list(line.split() for line in data) or data[:] = list(line.split() for line in data) In some profiling tests where I've compared them to list comprehensions, it seems the generator expressions are a little faster. The documentation also claims that generator expressions are more memory efficient. Is there any reason to use list comprehensions instead? (Sorry for the slight question hijack, but this is something I've been curious about.)
  11. List of free libraries

    Generally that might be true about C++ support, but what about a library like Twisted Matrix? Surely if you were familiar with it you wouldn't say that this library wasn't worth anything to game programming, and it's not easily usable with C++ as far as I can tell. As far as simplicity goes, that's always a good thing. But isn't the page just a list of links by category anyway? I'm not sure I see how additions of this type would dramatically increase the complexity. Maybe the core audience needs to be more open-minded, and have a chance of being exposed to more alternatives? A few months ago I would have considered myself part of that core audience, and I consider it a crime that I wasn't seriously exposed to such alternatives earlier. You could be doing others a big favor.
  12. List of free libraries

    How about some kind of information showing what languages each library has bindings for? Or better yet, listing all libraries that support a particular language under one section. For instance: Python: -pygame -pyopengl -pyode -twistedmatrix etc. That's something I would be interested to see. The existing categories don't have to be broken down necessarily, but it would also be nice to have libraries related by language found in the same place. At the moment, it seems the list is mainly geared towards C++. Maybe an organizational change would entice more people to list libraries for use with other languages?
  13. Weird problem with window resizing & SDL

    If you're brave enough, you can even venture into the source and see if you can find/fix the problem and submit a patch proposal. That's one of the great things about open source software... you can look at the source :-P.
  14. Weird problem with window resizing & SDL

    I noticed this problem after one of the more recent versions of SDL came out, and it just seems that it never was fixed for 1.2.8. I think it was fine in 1.2.6. Anyway, it's not a very serious issue, and most games would run in fullscreen anyway, right? I imagine nobody got around to the fix. Kind of a funny observation: If you maximize the window (title bar will now be higher than your screen) and then minimize, and then restore, it will end up back to normal too. This also only seems to happen in windows, as the linux build of my SDL apps resize normally.
  15. [size="5"]Premise This article will introduce some new concepts that will allow us eventually to model higher-level languages with simple bytecode instructions. The variable data associated with scripts will become part of a stack, which will enhance the higher level behavior we can achieve with a handful of opcodes, and allow us to evaluate complex mathematical expressions with relative ease. With these new facilities added, it will also be possible for our scripts to have controlled program flow, including patterns for if-else and loops, as well as script-based functions. [size="5"]The Reason for a Stack A stack is a natural representation for many types of systems involving states and procedural calculation. One such use as mentioned is in evaluating expressions that involve more than a single operation, since such expressions can be broken into parts and handled step by step. Consider a simple arithmetic expression: [bquote][font="Courier New"][color="#000080"]x + y * p[/color][/font][/bquote] Knowing simple rules for the ordering of operations, we can create a procedure for evaluating such an expression: [bquote][font="Courier New"][color="#000080"]multiply y by p add the result with x[/color][/font][/bquote] Place this expression in the context of variable assignment, and you will begin to see how it relates to programming: [bquote][font="Courier New"][color="#000080"]i = x + y * p multiply y by p add the result with x assign the final result as the value of i[/color][/font][/bquote] Notice that the procedure for evaluating such an expression mentions something that is only implicitly present in the expression itself. The procedure uses a term "result" which is understood to mean the result from the previous partial evaluation. It's almost an automatic assumption that we set aside the results of these sub-evaluations for use in further evaluations. What a stack will do for us is allow us to make this natural assumption. Being a LIFO (last in first out) data structure, we can push values onto it, pop them off to perform an operation, push the result back on, and so on until the parent evaluation is completed. By following a consistent set of evaluation rules, we can ensure that the stack will manage these "results" without us having to keep track of them. [size="5"]Implementing a Stack One of the first things that come to mind when thinking about stack implementations is that simple containers that could be used are already implemented as part of the standard library. They certainly are. However there will be certain features we need that they do not provide on their own. We will start with a simple stack that uses a vector for its implementation: template class ScopeStack { public: ScopeStack() {} ScopeStack(size_t reserveSize) { _stack.reserve(reserveSize); } // todo: scope interface // element interface void Push(const T& element) { _stack.push_back(element); } void Pop() { assert(!_stack.empty()); _stack.pop_back(); } // accessors const T& Top() const { assert(!_stack.empty()); return _stack.back(); } T& Top() { assert(!_stack.empty()); return _stack.back(); } const T& Get(size_t index) const { assert((index)Data()[0]; else ++_instr; _stack.Pop(); break; case op_jump_if_false: if (_stack.Top() == 0) _instr = _instrPtr + _instr->Data()[0]; else ++_instr; _stack.Pop(); break; These two instructions will behave as stated, moving to a new position after examining the value at the top of the stack, and popping it off before they are done. [size="5"]Higher-Level Constructs An explanation of how such low-level jump instructions relate to higher-level control structures is due. I will now illustrate a few examples of C-style constructs and their equivalent jump instruction patterns in our bytecode. Note that although our parser doesn't support commenting, I will be using them in the example scripts shown. The If-Else Clause: C: if (condition) { // do something } else { // do something else } Script: // evaluate condition jump_if_false A // A corresponds to an appropriate value to reach the position marked A: // do something jump B A: // do something else B: The While Loop: C: while (condition) { // do something } Script: A: // evaluate condition jump_if_false B // do something jump A B: The Do-While Loop: C: do { // do something } while (condition); Script: A: // do something // evaluate condition jump_if_true A As you can imagine, dealing with the low-level jump instructions can be tedious, particularly when figuring out the appropriate index to jump to. A more robust development tool would certainly include a way to label positions in the code, and have jump instructions reference the labels, as I've shown in the examples above. But the idea here was to show that in the appropriate patterns our jump instructions could represent higher-level constructs. [size="5"]Conditional Expressions In order to drive our new conditional jumps, we need to have instructions that yield conditional results. Instructions performing value or logical comparison make the most sense in this case, and most will function much like the instructions implementing binary operations in the earlier section. Unary: op_not: performs a logical negation of the top of the stack Binary: Each of these compares the top two values on the stack, popping the top and returning an appropriate true/false to the new top. op_and: performs logical conjunction op_or: performs logical disjunction op_equal op_not_equal op_greater op_greater_equal op_less op_less_equal This condition: [bquote][font="Courier New"][color="#000080"](x > 1) && (x 1) { result *= counter; --counter; } // output return result; } We can begin deconstruction by analyzing the number of variables we will need to track, and in this case it is two (counter and result). The parameter input can be simulated as a constant supplied at the very beginning of our script. We will use index 0 as the counter, and index 1 as the result: // initialize push_const Input // the actual value here is mutable assign 0 push_const 1 assign 1 Handling the final output is also an easy section to deconstruct: // output push_var 1 output end The tricky part now comes in while flattening out the iterative loop. However, all we need to do is use the pattern described earlier for simulating a while loop: A: // evaluate condition jump_if_false B // do something jump A B: The condition to be evaluated is simply a comparison between counter and the value 1: // counter > 1 push_var 0 push_const 1 greater And the action we are taking within the loop (do something) is multiplying result by counter, assigning the product to result, and then decrementing counter: // result *= counter; push_var 1 push_var 0 multiply assign 1 // --counter; push_var 0 push_const 1 subtract assign 0 Now we put all of these pieces together, to get this: push_const Input // the actual value here is mutable assign 0 push_const 1 assign 1 A: push_var 0 push_const 1 greater jump_if_false B push_var 1 push_var 0 multiply assign 1 push_var 0 push_const 1 subtract assign 0 jump A B: push_var 1 output end The result is 20 instructions (not counting the labels of course). To finalize the process, we replace A and B by the proper offsets in our list. A corresponds to the 5th instruction, and B corresponds to the 18th (remember not to count A: as an instruction). Using 0 to N-1 index convention, that gives the 5th instruction an offset of 4, and the 18th an offset of 17. A script that outputs the factorial of 4: push_const 4 assign 0 push_const 1 assign 1 push_var 0 push_const 1 greater jump_if_false 17 push_var 1 push_var 0 multiply assign 1 push_var 0 push_const 1 subtract assign 0 jump 4 push_var 1 output end [size="5"]Interesting Results If you play around with the script's initial input value, you'll come to realize something... It only outputs the correct value for an input of 5 or less! What happened? The reason for this is our stack contains elements of type char, which implies a range of -128 to 127. The factorial of 5 is 120, just barely making the cut. So what if we want to find the factorial of 7? We only have to make a small change. Behold the power of templates: class Execution { . . . private: ScopeStack _stack; // initially of type ScopeStack . . . }; The factorial of 7 now comes out to be 5040, as it should be. [size="5"]Conclusion In my opinion, these have most interesting concepts developed in our low-level implementation so far. Although it is obviously tedious work, scripts can now be written to express many of the simple higher-level concepts we have always taken for granted. I leave the expression of some other high-level constructs (such as functions) as exercises for the reader until next time. The tools for expressing simple functions are here. Check out the downloadable source for certain small changes in the test program we built last time in order to allow for our new instructions. One change was made to the parser to allow for underscores to be scanned as part of an opcode name. Other changes were made, but due to their trivial nature, they don't change the way the program behaves, and aren't really worth mentioning. Please give me a piece of your mind in the forum discussion, or via my website