如何将对象属性作为函数参数传递给lambda函数?

时间:2018-07-17 18:22:18

标签: python python-3.x python-3.6

我创建了两个返回排序列表的函数。他们两个都将包含Employee Class实例的列表作为参数。第一个按名称属性排序,第二个按年龄排序,它们都使用lambda函数

class Employee():

    allEmployees = []

    def __init__(self, name, age):
        self.name = name
        self.age = age
        Employee.allEmployees.append(self)


def sortEmployeesByName(some_list, name):
    return sorted(some_list, key=lambda employee: employee.name)

def sortEmployeesByAge(some_list, age):
    return sorted(some_list, key=lambda employee: employee.age)

如何仅创建一个函数sortEmployees,将属性作为第二个参数传递给我,并且还使用lambda函数?

例如

def sortEmployess(some_list, attribute):
    return sorted(some_list, key=lambda employee: employee.attribute)

4 个答案:

答案 0 :(得分:5)

您想要operator.attrgetter,不需要lambda。这样也应该会更好:

sorted(some_list, key=operator.attrgetter('name'))

答案 1 :(得分:2)

使用operator.attrgeter。我添加了__repr__方法来查看示例:

from operator import attrgetter

class Employee:

    allEmployees = []

    def __init__(self, name, age):
        self.name = name
        self.age = age
        Employee.allEmployees.append(self)

    def __repr__(self):
        return f'Employee({self.name}, {self.age})'

def sortEmployees(some_list, attribute):
    f = attrgetter(attribute)
    return sorted(some_list, key=f)

l = [Employee('John', 30),
Employee('Miranda', 20),
Employee('Paolo', 42)]

print(sortEmployees(Employee.allEmployees, 'name'))
print(sortEmployees(Employee.allEmployees, 'age'))

打印:

[Employee(John, 30), Employee(Miranda, 20), Employee(Paolo, 42)]
[Employee(Miranda, 20), Employee(John, 30), Employee(Paolo, 42)]

答案 2 :(得分:2)

这是使用operator.attrgetter的另一个版本。我认为在这里给Employee类一个.sort classmethod是有意义的。我已经“借用了” __repr__方法并从Andrej Kesely测试了数据。 ;)

from operator import attrgetter

class Employee:
    allEmployees = []

    def __init__(self, name, age):
        self.name = name
        self.age = age
        Employee.allEmployees.append(self)

    def __repr__(self):
        return f'Employee({self.name}, {self.age})'

    @classmethod
    def sort(cls, attr):
        return sorted(cls.allEmployees, key=attrgetter(attr))   

Employee('John', 30)
Employee('Miranda', 20)
Employee('Paolo', 42)

print(Employee.sort('name'))
print(Employee.sort('age'))

输出

[Employee(John, 30), Employee(Miranda, 20), Employee(Paolo, 42)]
[Employee(Miranda, 20), Employee(John, 30), Employee(Paolo, 42)]

关于operator.attrgetter的一件好事是,我们可以向它传递多个属性,并且它将返回一个属性元组。我们可以使用它在一次通过中按多个属性排序。但是我们需要稍微修改.sort方法。其他代码保持不变。

    @classmethod
    def sort(cls, *attrs):
        return sorted(cls.allEmployees, key=attrgetter(*attrs))


Employee('John', 30)
Employee('Miranda', 20)
Employee('Paolo', 42)
Employee('John', 20)

print(Employee.sort('name'))
print(Employee.sort('age'))
print(Employee.sort('name', 'age'))

输出

[Employee(John, 30), Employee(John, 20), Employee(Miranda, 20), Employee(Paolo, 42)]
[Employee(Miranda, 20), Employee(John, 20), Employee(John, 30), Employee(Paolo, 42)]
[Employee(John, 20), Employee(John, 30), Employee(Miranda, 20), Employee(Paolo, 42)]

答案 3 :(得分:1)

您可能不想这样做,但是无论如何,我将使用getattr向您展示如何操作:

getattr(object, name[, default])
  

返回 object 的命名属性的值。 name 必须为字符串。如果字符串是对象属性之一的名称,则结果是该属性的值。例如,getattr(x, 'foobar')等效于x.foobar。如果指定的属性不存在,则返回 default (如果提供),否则引发AttributeError

所以:

def sortEmployees(some_list, age, key_attr):
    return sorted(some_list, key=lambda employee: getattr(employee, key_attr))

但是,如果您唯一要使用的是排序键,则stdlib中的attrgetter会为您打包,因此您不需要lambda自己打包功能:

def sortEmployees(some_list, age, key_attr):
    return sorted(some_list, key=operator.attrgetter(key_attr))

您可能不想这样做的原因是,将数据和变量名混合在一起通常是个坏主意,as explained by Ned Batchelder better than I could.

您最终会看到一些东西-对人类读者和您的IDE而言,对像linters和类型检查器之类的静态检查器,甚至对优化器-如动态代码,即使它实际上是纯静态的。您将获得动态代码的所有缺点,而没有任何好处。

您甚至都不会获得更短的方法调用:

sortEmployeesByName(some_list, name)
sortEmployees(some_list, name, "name")

但是,这仅仅是“可能”而不是“绝对”的原因是,在某些情况下,相同的折衷方法却是相反的。

例如,如果您拥有15个而不是2个属性,则将代码复制,粘贴和编辑15次会严重违反DRY。或者,假设您正在动态地构建类或其实例,并且甚至在运行时才知道名称。

当然,您可以编写在类或实例创建时动态生成方法的代码,以便客户端代码可以静态使用它们。这是一个很棒的模式(在stdlib的不同地方使用)。但是对于一个简单的死案例来说,它可能会使事情变得非常复杂。 (很显然,典型的读者比找出getattr和描述符setattr来手动绑定方法的方法更容易找出__get__的含义。)而且它仍然不会帮助许多静态工具了解您类型的方法。

在许多此类情况下,解决方法是停止具有单独的命名属性,而不再具有单个属性,该属性是保存所有非相当属性的内容的字典。但是同样,这只是“很多”,而不是“全部”,权衡可以采取其他方式。例如,一个ORM类或类似Pandas DataFrame的行为,您希望能够将这些属性作为属性来访问。

因此,这就是存在该功能的原因:因为有时您需要它。在这种情况下,我认为您不需要它,但这只是一个判断电话。