Non-idempotent Python

10

3

Write a few lines of Python code, X, that does not reference any global variables, such that

def method():
    X
    print(a)

method()

prints 1 but

def method():
    X
    X
    print(a)

method()

prints 2.


So, I hate to be a stickler, but it seems like vars and locals are actually global variables in Python:

def test_global_1():
    global vars, locals
    vars = lambda: 2
    locals = lambda: 3

def test_global_2():
    print(vars())
    print(locals())

test_global_1()
test_global_2()

Also, it looks like people would like to see objective winning criteria for puzzles like this. Code length doesn't really feel right here so perhaps we could make a system of brownie points for various novel features of the code? I'm not sure exactly what these could be but here's a start:

  • +1 for really truly no globals (no vars or locals)
  • +1 for being the first to post a particular technique
  • +1 for the shortest solution posted
  • +1 for a solution involving only a single Python statement
  • +1 for interesting "hacks" like joining at lexical non-boundaries
  • +1 for not using exceptions

And if you can think of more you could edit this question to add to the list.

Can this problem be solved without using exceptions and without using globals like vars and locals? I suspect it can, though I haven't figured out exactly how yet...

Owen

Posted 2015-07-30T19:55:29.060

Reputation: 389

Question was closed 2015-07-31T19:25:10.717

Good puzzle! I made sure not to scroll down so I could solve it myself without seeing anyone's answers. :D – mbomb007 – 2015-07-30T21:36:50.193

1Thanks for the puzzles Owen, and welcome to the site. There is a rule on the site that all questions must have an objective winning condition, so you should probably add one. One possibility is shortest length of X, but there are other options. – isaacg – 2015-07-31T08:25:53.417

3"all questions must have an objective winning condition" - Stupid rule imho. Who cares about a "winner" when we all actually most enjoy the puzzling and learning from the different answers. – JimmyB – 2015-07-31T11:34:43.923

2Please add a [tag:code-golf] or [tag:popularity-contest] tag, depending on whether you want people to optimize for shortness code or for general popularity. I imagine code-golf is better for this challenge (popularity-contest is encouraged only for challenges that cannot be easily classified otherwise), but it's up to you. – apsillers – 2015-07-31T13:18:52.373

Ongoing meta discussion. – xnor – 2015-07-31T22:52:15.540

2You've added a scoring system, but also added the popularity contest tag, which means that a winner is decided by votes. What do you mean here? Perhaps you want votes just as a tiebreak? – xnor – 2015-08-02T02:13:39.300

You've added something saying 'vars' and 'locals' are in fact global variables, but I think your reasoning is flawed. Prior to your defining 'vars' and 'locals', they weren't global variables; this can be verified by looking at globals(). – Don Hatch – 2015-08-03T23:08:20.177

The restrictions you're adding are starting to make me think an answer is provably impossible. And I think your wording "a few lines of code" implies complete lines, which rules out clever hacks like joining at non-lexical boundaries. So, could I win by providing a compelling impossibility proof? – Don Hatch – 2015-08-04T02:00:31.213

@DonHatch An impossibility proof would count, yes. – Owen – 2015-08-04T02:02:59.797

Unfortunately question is on hold but I want to sneak in my answer anyway :P My code X would be a=method.a=method.__dict__.get('a',0)+1. – swenzel – 2015-08-04T20:17:09.383

No fun allowed guys – Robert Fraser – 2016-08-14T20:01:28.207

Answers

12

def method():
    if 'a' not in vars():a=0
    a+=1
    if 'a' not in vars():a=0
    a+=1
    print(a)

Initializes the variable a to 0 only if it's not already initialized in the variables table. Then, increments it.

More briefly (thanks to histocrat for len):

def method():
    a=len(vars())+1
    a=len(vars())+1
    print(a)

If the two copies of X could be on the same line, we could do

a=0;a+=1;a

which doubles to

a=0;a+=1;aa=0;a+=1;a

with the "sacrificial lamb" aa eating up the second variable assignment.

xnor

Posted 2015-07-30T19:55:29.060

Reputation: 115 687

3I don't want to be a spoilsport posting this so fast, so how about we try for shortest code? – xnor – 2015-07-30T20:03:10.953

3Slightly shorter variant: a=len(vars())+1 – histocrat – 2015-07-30T20:50:15.240

@histocrat Nice one, thanks! – xnor – 2015-07-30T21:03:38.407

9

Python

Thought of this solution, since try and except was the first way I thought of to determine if a variable existed yet or not.

def method():
    try:a+=1
    except:a=1
    print(a)

mbomb007

Posted 2015-07-30T19:55:29.060

Reputation: 21 944

5

Python 2

def method():
    exec'';locals()['a']=locals().get('a',0)+1
    exec'';locals()['a']=locals().get('a',0)+1
    print a

method()

Basically, when exec is encountered in Python 2, it causes a special flag (0x01) to be removed from method.func_code.co_flags, which makes locals assignments have an effect. I exploited this to implement nonlocal support in Python 2 (see line 43 for the xor that modifies the flag).

kirbyfan64sos

Posted 2015-07-30T19:55:29.060

Reputation: 8 730

Why not a = locals().get('a', 0) + 1? – Vincent – 2015-07-31T13:15:45.640

@Vincent I was tiree. :O Fixed. – kirbyfan64sos – 2015-07-31T14:15:05.293

In that case you don't need exec'' any more ;) – Vincent – 2015-07-31T14:20:29.843

@Vincent Eh, maybe I should just stick with the longer version. It felt more creative. Now it just seems a lot like a clone of the top-voted answer... :/ – kirbyfan64sos – 2015-07-31T14:23:01.330

2

My first idea (and then smooshing it) was:

def method():
    a=2if'a'in vars()else 1 
    a=2if'a'in vars()else 1 
    print(a)

But histocrat's answer seems optimal.

Don Hatch

Posted 2015-07-30T19:55:29.060

Reputation: 156

1

def method(a=[]):  
  a.append(a)  
  print len(a)

Edited in response to comment: a is a list of empty lists of length n, where n is the number of time you've called method. Calling this method twice prints 1 then 2.

WithScience

Posted 2015-07-30T19:55:29.060

Reputation: 119

7Putting a=[] as a parameter is outside the parameters for this challenge. – mbomb007 – 2015-07-31T02:57:17.577

Sorry, this is my answer, and it's not very good. This is (arguably) a surprisingly non-idempotent operation in python, but there's no way to slot it into the format provided for the challenge without also making the challenge trivial. – WithScience – 2015-07-31T17:29:57.553

Also, the challenge asks to print 1 or 2, not just two different things. – xnor – 2015-07-31T22:52:59.890

1

My attempt. Uses the math module to track if X is run once or twice.

def module():
  import sys
  if 'math' in sys.modules:
    a+=1
  else:
    a=1
  import math

  import sys
  if 'math' in sys.modules:
    a+=1
  else:
    a=1
  import math

  print(a)

module()

toto

Posted 2015-07-30T19:55:29.060

Reputation: 163

0

def method():
    #### X-block
    try:a
    except NameError:a=1
    else:a=2
    ####
    print(a)

The try block checks if the variable a is defined.
If variable not defined,(this is only when X-block is present once) then NameError Exception is raised.
If variable is defined, (this is when X-block is present twice) then else will be entered .

Kamehameha

Posted 2015-07-30T19:55:29.060

Reputation: 553

Yeah, that's the solution I found by searching Google. Then I created my current solution, which is shorter. – mbomb007 – 2015-07-30T21:46:43.040

@mbomb007 Yeah :P. Your way is shorter than using else – Kamehameha – 2015-07-30T21:50:39.783