0

In Python what is the idiomatic way for initializing Python's instance variable:

class Test:

    def __init__(self, a, b, c, d):
        self.a = a
        self.b = b
        self.c = c
        self.d = d

Or

class Test2:

    def __init__(self, data):
        self.a = data[0]
        self.b = data[1]
        self.c = data[2]
        self.d = data[3]

UPDATE: I have around 20 instance variables for a class named Link:

self.street 
self.anode 
self.bnode 
self.length 
self.setbackA 
self.setbackB 
self.bearingA 
self.bearingB 
self.ltype 
self.lanesAB 
self.leftAB 
self.rightAB 
self.speedAB 
self.fspdAB 
self.capacityAB 
self.lanesBA 
self.leftBA 
self.rightBA 
self.speedBA 
self.fspdBA 
self.capacityBA 
self.use 

Each variable is related to the class Link. Is there a recommended way of refactoring this?

9
  • 1
    I strongly prefer the first. It is more explicit and allows assignment out of order if you so choose, e.g. Test(b=4, a=3, d=1, c=1) Commented Sep 9, 2014 at 14:09
  • Also you can still apply an array to the constructor using Test(*[1, 2, 3, 4]). Commented Sep 9, 2014 at 14:10
  • 1
    If you have a class that requires 20 variables to instantiate, maybe you could consider refactoring? Commented Sep 9, 2014 at 14:12
  • 1
    @Sibi - if a method needs 20 variables, it's probably a sign that something's off about your architecture. You should instead either create a custom object/use dictionaries to make it easier to pass data around. Commented Sep 9, 2014 at 14:13
  • 1
    With 20+ variables (if necessary), you can create a dictionary cfg = {'a': 1, 'b':2, ...} and pass it as Test(**cfg). Commented Sep 9, 2014 at 14:14

2 Answers 2

4

The former, since it's more explicit about what the parameters are and what the object requires.

If you do need to pass in your data as a tuple, there's a shortcut you can use. Instead of doing the latter, or something like:

test = Test(data[0], data[1], data[2], data[3])

You can instead unpack the list/tuple and do:

test = Test(*data)

If you need to pass in a bunch of data (more then 4-5), you should look into either using optional/keyword arguments, creating a custom object to hold some of the data, or using a dictionary:

config = Config(a, b, c, d)
test = Test(e, f, config, foo=13, bar=True)

I would probably refactor your Link class to look like this:

class Node(object):
    def __init__(self, node, setback, bearing):
        self.node = node
        self.setback = setback 
        self.bearing = bearing 

class Connection(object):
    def __init__(self, lanes, left, right, speed, fspd, capacity):
        self.lanes = lanes 
        self.left = left 
        self.right = right 
        self.speed = speed 
        self.fspd = fspd 
        self.capacity = capacity 

class Link(object):
    def __init__(self, street, length, ltype, use, a, b, ab, ba):
        self.street = street 
        self.length = length 
        self.ltype = ltype 
        self.use = use 

        self.a = a 
        self.b = b 
        self.ab = ab 
        self.ba = ba 

I saw that you had some duplicate data, so pulled those off into a separate object. While this doesn't reduce on the number of fields you have, overall, it does make the parameters you need to pass in smaller.

Having a large number of fields isn't bad, but having a large number of parameters generally is. If you can write your methods in a way that they don't need a huge amount of parameters by bundling together data, then it doesn't really matter how many fields you have.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks, I have added some more details to the question.
@Sibi -- could you explain a little more on what 'AB' and 'BA' means?
AB means from A to B direction and B to A is the opposite direction. They may not necessarily have the same values.
2

Unpacking an array into a bunch of named variables suggests you should have started with named variables in the first place - stick with the first one.

There is only one reason you might want the second one here - you have something that is inconsiderately producing lists rather than objects. If that does happen:

data = get_data_in_list_form()
actual_data = Test(*data)

Can you group some of your data:

self.street
self.ltype
self.use
self.length

# .a and .b can be instances of NodeConnection
self.a.setback
self.a.bearing

self.b.setback
self.b.bearing
self.b.node

# .ab and .ba can be a separate class, "UniDirectionalLink
self.ab.lanes
self.ab.left
self.ab.right
self.ab.speed
self.ab.fspd
self.ab.capacity

self.ba.lanes
self.ba.left
self.ba.right
self.ba.speed
self.ba.fspd
self.ba.capacity

There's no need to do everything in a constructor here:

link = (
    Link(street=..., ltype=..., use=..., length=...)
        .starting_at(node_a, bearing=..., setback=...)
        .finishing_at(node_b, bearing=..., setback=...)
        .forward_route(lanes, left, right, speed, fspd, capacity)
        .reverse_route(lanes, left, right, speed, fspd, capacity)
)

1 Comment

Thanks, I have added some more details to the question.

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.