我创建了两个返回排序列表的函数。他们两个都将包含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)
答案 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的行为,您希望能够将这些属性作为属性来访问。
因此,这就是存在该功能的原因:因为有时您需要它。在这种情况下,我认为您不需要它,但这只是一个判断电话。