Forum Archive

A real newby question... Is shuffle a time travelling operation?

adrius42

These four lines are in a def setup function

self.emoji_original = self.emoji_names print(self.emoji_original) # result is alphabetic as expected shuffle(self.emoji_names) print(self.emoji_names, self.emoji_original)

Why does the second print show that both arrays have been shuffled?

JonB

Lists in python are objects, so when you modify one, both references are affected, since they refer to the same object.

x=[1,2,3]
y=x
print(id(y)==id(x))

Use .copy() to get a new copy that won't affect the original

z=x.copy()
print(id(z)==id(x))
adrius42

Aargh, this may explain some other faux pas I have been making.

Why??! This feels very counter-intuitive changing the meaning of equals in this way.
I guess way back in the mists of time the original Python developer had a reason!
So actually the operator was the time traveller! (Or rather the temporal cloner!)

I had previously declared self.emoji.original = [ ] so I was confident it had its own object id

I am getting ever more convinced that I do not understand the operation of “self”
Can you point me to some text that would help me... it feels like self is not always self, and often self is “not defined’ when I know it had been!?

I would have preferred
B = A transferred contents of A to B just thus once
B == A results in B becoming the same as A for all time

But then I am just wishing that the Python Language understood me, rather than me having to understand it!

To be clear this only applies to lists right?

A = 1
B = A
A = 2
Won’t change B to 2

So, are there any other types of variables where the operators change what they do?

It would be useful to know now! Rather than be struggling with coding issues ;-)

bennr01

@adrius42 said:

Why??! This feels very counter-intuitive changing the meaning of equals in this way.
I guess way back in the mists of time the original Python developer had a reason!
So actually the operator was the time traveller! (Or rather the temporal cloner!)

IIRC, most languages do this. It allows some usefull stuff which would otherwise be impossible.
For example, assume you had the following code:

def add_defaults(l):
    """add some default values"""
    if "value_default" not in l:
        l.append("value_default")


def remove_invalid_values(l):
    """remove all invalid values from list"""
    ...

some_values = ask_user_for_input()
add_default_values(some_values)
remove_invalid_values(some_values)
print(some_values)

Due to having the same list shared between the functions, we can avoid unneccessary assignments. This is both easier and way more efficient.

But the more important reason is consistency: list are objects. This is the same in many programming languages due to the way they work. And all non-primitve objects (maybe there are some exceptions, but none important) are not directly assigned to a variable. Instead, when you say some_object = SomeObject(), SomeObject() is created somewhere in your RAM and a reference is created which points to SomeObject(). This reference is then assigned to some_object. When you use some_var = some_object, some_var actually gets a copy of the reference, like some_var = some_int would do, but the copy of the reference still points to the same object. Thus, even if some_var is a copy of the reference in some_object, it still points to the same object.
Maybe this code can explain this a bit better:

class SomeClass(object):
    """some class"""
    def __init__(self, v):
        v = v

int_var_a = 1  # int_var_a is directly assigned the integer 1
int_var_b = int_var_a  # int_var_a is loaded, and the content is assigned to int_var_b
print(int_var_a == int_var_b)  # True, both are equal
int_var_a = 2
print(int_var_a == int_var_b)  # False

obj_var_a = SomeClass("value_1")  # an instance of SomeClass() is created and stored in the RAM. A reference pointing to SomeClass() is created and assigned to obj_var_a
obj_var_b = obj_var_a  # the >reference< if obj_car_a is copied to obj_var_b
print(obj_var_a.v == obj_var_b.v)  # True, because both obj_var_* point to the same object
obj_var_b.v = "some other value"  # the object is modified, but both obj_var_* still point to the same object
print(obj_var_a == obj_var_b)  # True, because both obj_var_* still point to the same object and both have been modified

I am getting ever more convinced that I do not understand the operation of “self”
Can you point me to some text that would help me... it feels like self is not always self, and often self is “not defined’ when I know it had been!?

self is in fact not a operation, but a variable name without any real meaning.
Here is some more code:


class MyClass(object):
    def method_a(self, x):
        # self points to the instance of MyClass() this method is called on. x is some other value
        pass

    def method_b(foo, x):
        # foo points to the instance of MyClass() this method is called on. x is some other value
        # this is because the first parameter of a bound method is always the object
        pass

    @staticmethod
    def some_static_method(x):
        #This is a static method. self is not defined here. x is some other value passed to this method.
        pass

    @classmethod
    def some_class_method(cls, x):
        # This is a class method. cls is not pointing to the instance of the object this is called on, but instead pointing to the class this is called on.
        # this is useful for alternative constructors

In other words: self is always defined if you name the first parameter/argument of a bound (more about that later) method self. If you name the first parameter/argument of a bound method foo, then foo would be the equivalent of self.
A bound method is a normal method of an instance of a class. In the above example, MyClass().method_a and MyClass().method_b are bound methods, because they are "normal" methods (not modified by @staticmethod or @classmethod) of an instance (MyClass()).
If you use MyClass.method_a or MyClass.method_b (please note the missing (), this is the class, not instances of the class), these are unbound methods, as they are not part of an instance. In this case, self must be specifically passed to the method.

Also, about self: you can think about self as some sort of namespace. self is unique per instance of a class. Changes to self persist between methods of the same instance, but not between methods of different instances.

I would have preferred
B = A transferred contents of A to B just thus once
B == A results in B becoming the same as A for all time

In python, == means a comparsion of equality.

But then I am just wishing that the Python Language understood me, rather than me having to understand it!

Dont worry, you are not the only one...

To be clear this only applies to lists right?

A = 1
B = A
A = 2
Won’t change B to 2

So, are there any other types of variables where the operators change what they do?

This behavior applies to all non-primitive types.
Some example of primitive types are int, long and float.
Non-primitive types are basically everything else, mostly instances of classes.
So, a={}; b=a; b["a"] = 2 would also modify a.
a=1; b=a; b=2 would not change a, because both b and a are primitive.
Strings are interesting. They are objects, but immutable. a=""; b=a; some_change(b) would change a too, but since neither a nor b can be changed, this is not a problem.

Small disclaimer: I am not involved with the development of python and can thus not guarantee that the above information is correct, but it should be.

Sorry for my bad english, it is not my native language. I still hope that this may be of help for you.

JonB

Out of curiosity, do you come from a Matlab background? Matlab is a little unique in that matricies act like primitives, not objects, and so assigning a matrix to another variable creates a copy (though, a sort of delayed copy that only takes the performance penalty if you modify it).

This for me led to many mistakes when first learning python.

adrius42

Much older! Algol then, Basic. More recently Arduino and Sphero!
I am restarting my coding activities and try to keep my brain active!
And to get ahead of my grandchildren for a millisecond or two!

adrius42

@bennr01 Thank you muchly for taking the time to answer my musings.

I assure you, that your English is not the problem, in my comprehension of these concepts, it is much more my lack of understanding!

I will spend the time to extract the wisdom from your response. Thankyou!

cvp

@adrius42 Very old, indeed. I knew also Algol, like Fortran, Pl/1, Cobol, Apl and all IBM Main frames languages, all were not object oriented, thus all variables were "native", thus this difference did not exist.
Welcome in the world of Pythonista, marvelous app for retired people 😂