0

我有两个不同的对象。其中一个在列表或元组属性中包装了 N 个其他类型的对象。假设教室里的学生:

class Student:
    def __init__(self, name):
        self.name = name

class ClassRoom:
    def __init__(self, students):
        self.students = students

当然,我们有大量 Student 和 ClassRoom 实例:

john, sam = Student('John'), Student('Sam')
patrick, michael, bill = Student('Patrick'), Student('Michael'), Student('Bill')
klass1 = ClassRoom([john, sam])
klass2 = ClassRoom([patrick, michael, bill])

只需考虑每个学生的名字都是独一无二的,并且您无法通过引用访问学生的教室,如下所示:

sam.get_classroom() # Student class doesn't support this :(

我们有一个辅助函数来完成这项工作:

def get_classroom_by_student(klasses, student_name):
    for klass in klasses:
        for student in klass.students:
            if student.name==student_name:
                return klass
                # Or yield if a student belongs to more than one class

sams_class = get_classroom_by_student([klass1, klass2], 'Sam')
bills_class = get_classroom_by_student([klass1, klass2], 'Bill')

既然“平面优于嵌套”,我该如何创建一个高效的生成器,或者是否有一些 Python 的方法来实现这个辅助函数?

4

3 回答 3

1

假设您不想更改数据模型,您可以像这样重写您的函数:

def get_classroom_by_student(klasses, student_name):
    for klass in klasses:
        for student in klass.students:
            if student.name==student_name:
                yield klass

这或多或少等同于

def get_classroom_by_student(klasses, student_name):
    combinations = []
    for klass in klasses:
        for student in klass.students:
            if student.name==student_name:
                combinations.append(klass)
    return combinations

但是,清洁剂如下:

def get_classroom_by_student(klasses, student):
    for klass in klasses:
        for s in klass.students:
            if s is student:
                yield klass

这可以使用嵌套列表推导重写:

def get_classroom_by_student(klasses, student):
    return [klass for klass in klasses for s in klass.students if s is student]

甚至更短

def get_classroom_by_student(klasses, student):
    return [klass for klass in klasses if student in klass.students]
于 2012-09-28T19:40:49.760 回答
0

这个怎么样:

class Student:
    def __init__(self, name):
        self.name = name

class ClassRoom:
    def __init__(self, students):
        self.students = students

john, sam = Student('John'), Student('Sam')
patrick, michael, bill = Student('Patrick'), Student('Michael'), Student('Bill')

klass1 = ClassRoom([john, sam])
klass2 = ClassRoom([patrick, michael, bill])


def where_is(student, klasses):
    return next((x for x in klasses if student in x.students), None)

assert klass1 is where_is(john, [klass1, klass2])
assert klass2 is where_is(patrick, [klass1, klass2])

nobody = Student('foo')
assert None is where_is(nobody, [klass1, klass2])

对于产生的版本,只需省略next并返回生成器:

def where_is(student, klasses):
    return (x for x in klasses if student in x.students)

for klass in where_is(john, [klass1, klass2]):
    print klass
于 2012-09-28T19:50:41.197 回答
0

所以这没有那么多的嵌套。本质上,我们构建了一个字典 m,将每个学生映射到其教室。在我看来,它不是那么可读或干净。

您可能想构建一次字典,然后使用它来查找学生课程。我强烈建议检查Django 中的模型。他们以更清洁的方式完成您正在寻找的工作。Django 的优点在于,如果您在教室与学生之间放置外键关系,它会自动向学生对象添加方法,让您查找他们的教室。因此,它为您完成所有工作。

from itertools import chain

def flatten(items):
    return list(chain.from_iterable(items))

def get_classroom_by_student(klasses, student_name):
    m = flatten([dict.fromkeys(k.students, k).items() for k in klasses])
    m = dict([(s.name, c) for s,c in m])
    return m[student_name]
于 2012-09-28T19:55:03.060 回答