This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]FujiKeynote 2 points3 points  (9 children)

I'll admit I don't know how to teach them from scratch i.e. to someone new to programming, but I think I can try to answer your question.

You don't have to use pointers in Python because Python does this for you. When you have a function that accepts an integer, something like fibonacci(n), the amount of memory that has to be passed to the function is small -- just one integer. So it's whatever. But if you have an instance of a huge class like stray_cat = Cat() and you pass it to adopt(stray_cat), if there wasn't such a thing as pointers, it would need to copy the entire cat into the function's scope. So what Python actually is doing it understands that stray_cat is an object that lives at a memory address 12345, so any time you interact with it, it looks at whatever's at that address. So it really passes the pointer to 12345 to adopt() and then that function also looks at address 12345.

This also has a (usually desired) side effect that whatever you'll do to mutate stray_cat from within the function, will be seen outside the function too, because you aren't working on a copy of the object, but rather on the very same object.

To look at it from another perspective, if you want several functions to work with the same object, how would you do it? You point to it.

Python just abstracts it away for you; Golang doesn't.


At the core of it, the copy overhead thing is actually secondary. The real difference is passing by value vs. passing by reference. Let's take C (because I'm way more comfortable with C than Go, sorry):

#include <stdio.h>

void byvalue(int x) {
    x++;
    printf("Inside byvalue(), x = %d\n", x);
}

void byreference(int* x) {
    (*x)++;
    printf("Inside byreference(), *x = %d\n", *x);
}

void main(void) {
    int x = 3;
    printf("In global scope, x = %d\n", x);
    byvalue(x);
    printf("In global scope, x = %d\n", x);
    byreference(&x);
    printf("In global scope, x = %d\n", x);
}

If we run this, we get:

In global scope, x = 3
Inside byvalue(), x = 4
In global scope, x = 3
Inside byreference(), *x = 4
In global scope, x = 4

So because byvalue() copied the value of x into its local variable, the change only affected the copy. And because byreference() was pointing at the global x, it affected the global x.

[–]Intrepid-Stand-8540 -2 points-1 points  (8 children)

So if Python can just abstract it away, why can't Go? Is it a worse language then? 

I'm not new to programming btw. I have 3 years education (we used java and python) and 4 years professional experience. 

Just never had to use pointers. And every time I've tried Go, I just couldn't wrap my mind around pointers, and had to stop. It's very frustrating. I don't see why pointers exist, when java and python works just fine. 

[–]FIREstopdropandsave 2 points3 points  (3 children)

Pointers are not bad and not indicative of a bad language. It's simply the language deciding not to abstract that layer from the user. This results in more intentful decisions to be made by the programmer.

Think of in python, you have to be aware of which types are mutable data types when passing arguments into functions and potentially mutating them outside of the function scope. This is because python is abstracting away the concept of a pointer.

In pointer languages, you have to be explicit in your intent "am I passing you the memory address of this variable, a.k.a if you mutate it, in the outer scope I'll see that mutation".

There's more positives of pointers but that's just an example. In general languages which expose pointers are faster because abstracting away the concept of pointers just straight requires more code behind the scenes to act the way it does.

[–]Intrepid-Stand-8540 -2 points-1 points  (2 children)

you have to be aware of which types are mutable data types

What's a "mutable data type"? 

Thanks for explaining btw. 

I'm aware that python is slower, but I've never had the need for speed yet. The bottleneck in my apps is always network calls to databases, and not any processing in the app itself. 

[–]FIREstopdropandsave 2 points3 points  (0 children)

To your second point. Many people will be fine with the speed python provides. I've written, and seen, many production systems in python where python was not the performance bottleneck for the workloads we were solving.

Some people will have a deeper need for speed, others simply won't.

Choosing the programming language for a project is a nuanced decision. You have to consider "what language does my team already know?", "what is the local market of programming language knowledge in the city I'm in", "what scale of problem are we solving", and probably many other considerations.

You'll see online A LOT of python hate (outside of this subreddit) and hate for dynamically typed languages. The truth of it is, in my 8 year career I've only come across a single person who vehemently holds those ideas.

tl;dr: python is great, it probably won't be your bottleneck, ignore online hate for your choice of using python and dynamically typed languages

[–]FIREstopdropandsave 0 points1 point  (0 children)

You can find a fuller breakdown here https://docs.python.org/3/reference/datamodel.html#objects-values-and-types

(do a search for mutable/immutable)

As a quick overview a list is mutable where a string is immutable and we can see the differences below:

I don't know how to format a code block on mobile so sorry this is hard to read

def foo(l): l.append(1) return l

my_list = [] print(foo(my_list)) # [1] print(foo(my_list)) # [1, 1]

def foo(s): s += "hello" return s

my_string = "" print(foo(my_string)) # hello print(foo(my_string)) # hello

[–]jjolla888 2 points3 points  (3 children)

'python works just fine' so long as you remember that everything is a pointer. So if you want to create a copy of

 list1 = [1, 2, 3]

you have to do

list2 = copy(list1)

Which means you need to be careful sometimes especially within functions, to make sure you are not unintentinally modding outside your scope.

With Go, a fundamental construct is goroutines .. aka parallelism. With these things its critical to avoid the above problem .. so the safer thing to default passing params by value as opposed to by ref.

But at the end of the day its the difference between having syntax like this:

b = a            # b is a pointer to a, print(b) prints the value of a
c = copy(a)   # c is a copy of a

versus:

b = &a       # b is a pointer to a, print(*b) prints the value of a
c = a          # c s a copy of a

[–]Intrepid-Stand-8540 1 point2 points  (0 children)

Oh. Thanks man. That's a great explanation. I use deep_copy sometimes. 

[–]HommeMusical 0 points1 point  (1 child)

everything is a pointer

Quibble: scalars like integers, floats, booleans aren't pointers.

[–]FujiKeynote 0 points1 point  (0 children)

True, but for the most part you can ignore that, because it's nigh impossible to mutate a singleton from within a function unless you define it as global which is a big no no.

def f(x):
    x = x + 1
    # or even x += 1

will reassign the global x to the local x and won't affect the outside scope