1
Class BankAccount:

    def __init__(self, accID, name, balance):
        self.accID = accID
        self.name = name
        self.balance = balance

In the code above, why is it that I'm required to map all of my arguments to instance variables? This just seems like a waste of time. I'm using the exact same names, and the only difference is that I prepend them with the 'self' keyword. Why can't I just use the argument names? In python, things seem to get rather unwieldy when an object's constructor takes in many arguments.

I understand the obvious -- that it is a requirement of the langugage -- but I was wondering why virtually all OO languages are implemented this way.

1
  • 2
    By "virtually all" - you mean C-like?
    – Ordous
    Apr 17, 2015 at 16:03

6 Answers 6

3

There are cases where a simple one-to-one mapping is not appropriate. Automatically assigning parameters to instance variables only works sometimes, not all of the time. Perhaps validation is needed, or an exception might be thrown. Maybe a parameter must be scaled, or another object created.

Consider a Java BigDecimal. Internally it has a BigInteger and an integer scale. I see exactly one constructor out of many that accepts those two parameter types. Why go through the effort of having an automatic mapping that is of niche use a lot of the time? Most of those constructors likely have to perform additional processing to convert the parameters into its internal state. Those are a type of convenience constructor.

Furthermore, such a feature might encourage leaky abstractions. While that feature alone is not leaky, it could encourage lazy programmers to make all of the constructors they write to accept precisely the object's internal state as parameters.

1
  • 3
    An example I am currently working on: The constructor takes two parameters, an IP address and a port, and uses those to create a socket connection. The instance variable is the socket connection and the IP/Port is discarded. Apr 17, 2015 at 18:07
3

Because of inheritance.

In a language without inheritance, the constructor could simply map its arguments to the object's fields in a one-to-one manner. If the mapping isn't one-to-one, you can always hide the constructor and provide a static method to do the work and pass the final values to the constructor. Case in point, this is how many functional languages handle it.

However, with inheritance, you run into a problem - you need to initialize the parent class's fields. The language could force you to declare the subclass constructor such that it includes all of the parent class's fields, but which values do you pass? The logic for initializing them is locked away in the parent class's static factory method. You'd need to reimplement that logic, and that could be impossible if you don't have the source code and the logic is undocumented.

That said, there's no reason why the language can't provide syntactic sugar for it.

1
  • This is unclear for me, and I would like to see an example that shows more directly the problem you are talking about. You seem to make implicit assumptions about the language or the static factory methods. I think it is possible to write one-to-one mappings while having inheritance, so maybe I did not understand what you mean.
    – coredump
    Apr 20, 2015 at 8:24
1

The purpose of a constructor method is to construct an object. Your constructor parameters are scoped to the constructor method, so unless you save them in instance variables, they are lost once your constructor method goes out of scope. This is how parameters work in any method; they are local to the method, not global to the class.

Your constructor method might simply map the parameter values to member variables, but it might do something completely different. Your programming paradigm needs the flexibility to allow you to do both.

Constructor methods typically don't return anything. An ordinary method can perform useful work with parameters without involving instance variables, since you can actually return something from the method.

1

You usually don't need to. Scala, for example, just keeps the arguments to the primary constructor in scope throughout the object's lifetime. It turns out, the vast majority of the time that's all you need, especially if you support default values.

However, you still have to support those other cases that the other answers enumerated. Most languages optimized their design to cover the largest number of cases consistently, rather than optimizing for brevity in the most frequent case at the expense of more complexity and less consistency in handling the less frequent cases.

Partially, this is because as a language designer, it's difficult to tell what will actually be the most frequent case in the wild, or what the real-world consequences would be of sometimes holding onto a reference to a constructor argument that you no longer need.

0

I understand the obvious -- that it is a requirement of the language -- but I was wondering why virtually all OO languages are implemented this way.

Because there is a difference between the type (or type constructor) and the constructor (or data constructor). By mixing these concepts, you're making assumptions about what the programmer wants - preventing them from having multiple constructors for the same type.

That said, C# 6 is introducing primary constructors to do this sort of stuff:

https://msdn.microsoft.com/en-us/magazine/dn802602.aspx

I also did this in an iteration of Tangent because I explicitly wanted to unify type constructors and data constructors, because by tying a single data constructor to a type made it much easier (read: possible) to mix the types together and still get a constructable type.

1
  • Somehow, I find this answer very confusing. I know type constructors from parametric polymorphism as things that are of kind * → *, and I know object constructors as a kind of factory function in OOP. How does the above example confuse these? Python doesn't even have type constructors! I get C#'s/Scala's primary constructors and why they are an answer here, but how does that make types mixable? Why are you going off on a Tangent? What is that? Would you please edit this answer into something I can read without having to consult my mind-reading crystal ball?
    – amon
    Apr 17, 2015 at 19:07
0

From a language structure point of view: Because of scope. Parameters passed in to a function are only valid and accessible within that function. Instance variables are valid and accessible in every function.

Furthermore, by definition function parameters are passed in to the function from a caller. So they do not reside within the object: they reside in the caller. They must, of necessity, represent two different physical areas in memory.

I suppose that in principle you could have a language where any parameters in a function that have the same name as object variables are automatically copied before the first line of the function is executed. My first thought is that that could cause trouble, because there are surely times when you would not want such an automatic copy. There might well be validity tests that should be done before saving any data. We might want to test if a new value equals the old value and so some logging. Etc. I suppose someone could reply that in that case you could just give the parameter a different name.

For two or three variables, I generally see it as no big deal. Yes, when there are 15 variables passed in and all have to be assigned to the correct object variable, it's a pain. I'd be interested in seeing a language that had a feature to do this cleanly.

Frankly, what I usually do is create the object and then have a bunch of assignment statements. Like

myfoo=new Foo();
myfoo.bar=42;
myfoo.plugh="fwacbar";
... etc ...

This can be more clear than having a long string of values in the constructor, where it may not be obvious which variable each value goes to. (Especially small integers and Booleans.) It's only slightly more typing to write the assignment statements rather than a long parameter list. In the not-uncommon case where the constructor is only called once or twice, it might be less typing, as it eliminates the parameter list and simply moves the assignments from the constructor to the caller.

Of course it has the drawback that it only works if all the values are straight assignments with no validation or manipulation. And in some cases we can say that a constructor with all the values produces a valid, usable object; while this technique means it almost certainly does not, which some consider a violation of the constructor contract.

3
  • 1
    Initializing after construction has too many issues: It violates encapsulation. It requires your object to be mutable. The partially constructed object might initially be in an illegal state. It throws away a lot of type safety. It requires your object members to be public and read-write. The object might have constraints that can't be expressed via types, e.g. that a string must be non-empty or that a number falls inside a certain range. With direct assignment, these constraints can't generally be checked. Don't give up; if your constructor becomes unwieldy, use the Builder or Factory Pattern
    – amon
    Apr 17, 2015 at 18:45
  • I don't see how it throws away type safety. Most of the rest of what you said I agree with and conceded above. It works in situations where many constraints apply, i.e. none of the above issues come up. But that's actually a pretty large number of cases that I come across. If an object calls for a long parameter list in a constructor, odds are that most of that data is of the in-and-out type, i.e. the object is a place to hold bunches of data about a customer or a product or whatever, and most of that data doesn't affect the logic, it just gets read, stored, and updated.
    – Jay
    Apr 17, 2015 at 21:29
  • Plain Old Data 'POD' objects are indeed structs that you just fill up from a database or form and hand around.When you're constructing a collaboration graph, your objects must always be in a legal state. Apr 17, 2015 at 22:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.