Asked  7 Months ago    Answers:  5   Viewed   37 times

Variables in Python are just pointers, as far as I know.

Based on this rule, I can assume that the result for this code snippet:

i = 5
j = i
j = 3 
print(i)

would be 3. But I got an unexpected result for me, it was 5.

Moreover, my Python book does cover this example:

i = [1,2,3]
j = i
i[0] = 5
print(j)

the result would be [5,2,3].

What am I understanding wrong?

 Answers

61

We call them references. They work like this

i = 5     # create int(5) instance, bind it to i
j = i     # bind j to the same int as i
j = 3     # create int(3) instance, bind it to j
print i   # i still bound to the int(5), j bound to the int(3)

Small ints are interned, but that isn't important to this explanation

i = [1,2,3]   # create the list instance, and bind it to i
j = i         # bind j to the same list as i
i[0] = 5      # change the first item of i
print j       # j is still bound to the same list as i
Tuesday, June 1, 2021
 
letrollpoilu
answered 7 Months ago
75

I want form.data['field'] and form.field.value to always have the same value

This is feasible, because it involves decorated names and indexing -- i.e., completely different constructs from the barenames a and b that you're asking about, and for with your request is utterly impossible. Why ask for something impossible and totally different from the (possible) thing you actually want?!

Maybe you don't realize how drastically different barenames and decorated names are. When you refer to a barename a, you're getting exactly the object a was last bound to in this scope (or an exception if it wasn't bound in this scope) -- this is such a deep and fundamental aspect of Python that it can't possibly be subverted. When you refer to a decorated name x.y, you're asking an object (the object x refers to) to please supply "the y attribute" -- and in response to that request, the object can perform totally arbitrary computations (and indexing is quite similar: it also allows arbitrary computations to be performed in response).

Now, your "actual desiderata" example is mysterious because in each case two levels of indexing or attribute-getting are involved, so the subtlety you crave could be introduced in many ways. What other attributes is form.field suppose to have, for example, besides value? Without that further .value computations, possibilities would include:

class Form(object):
   ...
   def __getattr__(self, name):
       return self.data[name]

and

class Form(object):
   ...
   @property
   def data(self):
       return self.__dict__

The presence of .value suggests picking the first form, plus a kind-of-useless wrapper:

class KouWrap(object):
   def __init__(self, value):
       self.value = value

class Form(object):
   ...
   def __getattr__(self, name):
       return KouWrap(self.data[name])

If assignments such form.field.value = 23 is also supposed to set the entry in form.data, then the wrapper must become more complex indeed, and not all that useless:

class MciWrap(object):
   def __init__(self, data, k):
       self._data = data
       self._k = k
   @property
   def value(self):
       return self._data[self._k]
   @value.setter
   def value(self, v)
       self._data[self._k] = v

class Form(object):
   ...
   def __getattr__(self, name):
       return MciWrap(self.data, name)

The latter example is roughly as close as it gets, in Python, to the sense of "a pointer" as you seem to want -- but it's crucial to understand that such subtleties can ever only work with indexing and/or decorated names, never with barenames as you originally asked!

Wednesday, June 2, 2021
 
Avicinnian
answered 7 Months ago
17

All the standard text refers to creating an "unnamed struct":

struct {
   int hi;
   int bye;
};

Just a nice friendly type, with no accessible name.

In a standard way, it could be instantiated as a member like this:

struct Foo {
   struct {
      int hi;
      int bye;
   } bar;
};

int main()
{
   Foo f;
   f.bar.hi = 3;
}

But an "anonymous struct" is subtly different — it's the combination of an "unnamed struct" and the fact that you magically get members out of it in the parent object:

struct Foo {
   struct {
      int hi;
      int bye;
   }; // <--- no member name!
};

int main()
{
   Foo f;
   f.hi = 3;
}

Converse to intuition, this does not merely create an unnamed struct that's nested witin Foo, but also automatically gives you an "anonymous member" of sorts which makes the members accessible within the parent object.

It is this functionality that is non-standard. GCC does support it, and so does Visual C++. Windows API headers make use of this feature by default, but you can specify that you don't want it by adding #define NONAMELESSUNION before including the Windows header files.

Compare with the standard functionality of "anonymous unions" which do a similar thing:

struct Foo {
   union {
      int hi;
      int bye;
   }; // <--- no member name!
};

int main()
{
   Foo f;
   f.hi = 3;
}

It appears that, though the term "unnamed" refers to the type (i.e. "the class" or "the struct") itself, the term "anonymous" refers instead to the actual instantiated member (using an older meaning of "the struct" that's closer to "an object of some structy type"). This was likely the root of your initial confusion.

Sunday, June 6, 2021
 
conmen
answered 7 Months ago
87

C was conceived as a language in which pointers and integers were very intimately related, with the exact relationship depending upon the target platform. The relationship between pointers and integers made the language very suitable for purposes of low-level or systems programming. For purposes of discussion below, I'll thus call this language "Low-Level C" [LLC].

The C Standards Committee wrote up a description of a different language, where such a relationship is not expressly forbidden, but is not acknowledged in any useful fashion, even when an implementation generates code for a target and application field where such a relationship would be useful. I'll call this language "High Level Only C" [HLOC].

In the days when the Standard was written, most things that called themselves C implementations processed a dialect of LLC. Most useful compilers process a dialect which defines useful semantics in more cases than HLOC, but not as many as LLC. Whether pointers behave more like integers or more like abstract mystical entities depends upon which exact dialect one is using. If one is doing systems programming, it is reasonable to view C as treating pointers and integers as intimately related, because LLC dialects suitable for that purpose do so, and HLOC dialects that don't do so aren't suitable for that purpose. When doing high-end number crunching, however, one would far more often being using dialects of HLOC which do not recognize such a relationship.

The real problem, and source of so much contention, lies in the fact that LLC and HLOC are increasingly divergent, and yet are both referred to by the name C.

Friday, June 11, 2021
 
WooDzu
answered 6 Months ago
75
def get_int(prompt):
    while True:
        try:
            return int(input(prompt))
        except ValueError:   # not an int!
            pass   # try again

residents = get_int("How many people live at your house? ")

Edit: rather than having named variables for each person, you can use a list:

resident_names = [input("Name of resident {}: ".format(i)) for i in range(1, residents + 1)]
Friday, September 3, 2021
 
frontendzzzguy
answered 3 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share