0

This follows on from a previous query of mine Static initializer in Python I am trying to find an object from one of it's properties as I would with a static constructor in java:

public class TestBuilder {
private String uid;
private String name;
private double speed;
private static ArrayList<TestBuilder> types = new ArrayList<TestBuilder>();
public static final TestBuilder SLEEPY;
public static final TestBuilder SPEEDY;

static {
    SLEEPY = new TestBuilder("1", "slow test", 500.00);
    SPEEDY = new TestBuilder("2", "fast test", 2000.00);
}

private TestBuilder(String uid, String name, double speed){
    this.uid = uid;
    this.name = name;
    this.speed = speed;
    types.add(this);
}

public static TestBuilder getBuilderFromName(String s){
    for (int i = 0; i < types.size(); i++){
        if (((TestBuilder) types.get(i)).name.equals(s)){
            return (TestBuilder) types.get(i);
        }
    }
    return null;
}

In python I have tried:

class TestBuilder:
    uid = str()
    name = str()
    speed = float()
    types = []

    def __init__(self, uid, name, speed):
        self.uid = uid
        self.name = name
        self.speed = speed
        self.types.append(self)

    @classmethod
    def lookupType(cls, name):
        for item in cls.types:
            if item.name == name:
                return cls

TestBuilder.SLEEPY = TestBuilder("1","slow test", 500.0)
TestBuilder.SPEEDY = TestBuilder("2","fast test", 2000.0)

Then in a test module:

class Tester(unittest.TestCase):
    def test_builder(self):
        dummy = TestBuilder
        ref = dummy.SPEEDY
        objfound = dummy.lookupType("slow test")
        logger.debug("--test_builder() name: "+objfound.name)

While the correct object is found in the lookupType method the resultant classfound name property is empty when accessed in the test. How can I access the correct TestBuilder object given one of it's properties?

Community
  • 1
  • 1
Don Smythe
  • 9,234
  • 14
  • 62
  • 105

1 Answers1

3

You are returning the class, not the instance you found:

@classmethod
def lookupType(cls, name):
    for item in cls.types:
        if item.name == name:
            return cls
            #      ^^^

Return the item instead:

return item

Instead of using a list, register your classes using a dictionary so you can simply look them up by name:

class TestBuilder:
    types = {}

    def __init__(self, uid, name, speed):
        self.uid = uid
        self.name = name
        self.speed = speed
        self.types[name] = self

    @classmethod
    def lookupType(cls, name):
        return cls.types.get(name)

Note that your uid = str() and speed = float() lines simply create class attributes set to an empty string and 0.0 respectively. Those lines do not declare a type and Python won't stop your code from assigning different objects to those attributes at the instance level.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • Much appreciated. NB I changed the types declaration to types = {} – Don Smythe Dec 14 '14 at 03:01
  • @DonSmythe: oops, that's a typo on my part, corrected. – Martijn Pieters Dec 14 '14 at 03:01
  • 1
    I would suggest using a variable name other than **types**, as it is the name of a module within the standard Python library. – Yani Dec 14 '14 at 06:53
  • @Yani: that hardly matters here; it is a class attribute here, so it is namespaced. – Martijn Pieters Dec 14 '14 at 10:13
  • @MartijnPieters - True, but it's good practice in my opinion. – Yani Dec 15 '14 at 09:32
  • @Yani: there are too many names to avoid and you'll never remember them all. `types` is not used that often and certainly as a class attribute is **more than fine**. – Martijn Pieters Dec 15 '14 at 09:33
  • @MartijnPieters - Okay, each to their own. My personal preference is to use variable names which do not match objects from the standard Python library, and when I do use them, I always include a trailing underscore `types_`. It's a friendly reminder to anyone reading the code and it helps keep things consistent. I am not saying you're wrong, I am simply offering the user my perspective. – Yani Dec 15 '14 at 09:55
  • @Yani: I use `type_` all the time; but to extend it to standard library names when naming attributes is just not required at all, just like you can safely use custom globals (that do not mask libraries) and class or instance attributes that have the same name. And again, you'll never remember them all. Do you do the same for `user`, `site`, `new`, `copy`, `code` and `test` for example? Note that the stdlib *itself* certainly doesn't; there are examples there that use these same names as attributes or function arguments. – Martijn Pieters Dec 15 '14 at 10:27