【问题标题】:Using lambda function to change value of an attribute使用 lambda 函数更改属性的值
【发布时间】:2014-08-02 13:57:18
【问题描述】:

我可以使用 lambda 函数循环遍历类对象列表并更改属性的值(对于所有对象或满足特定条件的对象)?

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

student1 = Student('StudOne',18)
student2 = Student('StudTwo',20)
student3 = Student('StudThree',29)
students = [student1,student2,student3]

new_list_of_students = map(lambda student:student.age+=3,students)

【问题讨论】:

  • @Ned 'def change_age(student): student.age +=3 return student new_students_age_list = map(change_age,students)'
  • 如您所知,lambda 不允许做作业。所以你必须在类中添加一个合适的方法并从 lambda 中调用它。如果你不能改变类,你总是可以在实例中添加一个方法并获得相同的效果
  • @pythondev 如果你这样做,那么increased_students_age_list 的内容与students 完全相同。您有两个相同的列表,这没有意义。

标签: python lambda


【解决方案1】:

不幸的是,这是不可能的,因为body of a lambda 只允许simple expressions,而student.age += 3statement。所以你不能在那里使用 lambda。但是,您仍然可以使用地图解决方案:

def incrementAge (student):
    student.age += 3
    return student

students2 = map(incrementAge, students)

请注意,students2 将包含与students 相同的学生,因此您实际上不需要捕获输出(或从incrementAge 返回某些内容)。另请注意,在 Python 3 中,map 返回一个生成器,您需要先对其进行迭代。你可以在上面调用list() 来做到这一点:list(map(…))

最后,一个更好的解决方案是使用一个简单的循环。这样一来,您就不需要使用函数或创建重复的学生列表,而且您的意图也会非常明确:

for student in students:
    student.age += 3

【讨论】:

  • 除了函数def行中的空格,我真的很喜欢这个答案! :)
  • @NedBatchelder 呵呵,我猜你不喜欢那个空间——但我喜欢在那儿放一个用于定义;P
【解决方案2】:

使用简单的 for 循环检索 students 来更新 age 就像其他人所说的那样已经足够好了,但是如果您仍想使用 lambda 来更新值,您可能需要利用exec() 函数:

_ = list(map(lambda student: exec("student.age+=3"), students))
for _ in students: print(_.age)

输出:

21 23 32

在这种情况下,实际更新的是exec(),而map() 只是产生None。所以返回的结果没有意义,我放了一个_ 来澄清这一点。更简洁的形式是:

list(map(lambda _: exec("_.age+=3"), students))

此外,如果只考虑你想做什么,你根本不需要使用map()(虽然可能更令人困惑):

[(lambda _: exec("_.age += 3"))(_) for _ in students]

此外,lambda 也可以被丢弃:

[exec("_.age += 3") for _ in students]

如您所见,上面没有任何“技巧”代码比其他答案发布的代码更简洁:

for s in students:
    s.age += 3

所以也许所谓的“单线”只是在玩得开心时才有用...... :)

【讨论】:

    【解决方案3】:

    Lambda 函数只能包含表达式,不能包含语句。 Python 中的赋值是一个语句。 Lambda 不能做赋值。此外,作业语句不会评估它们的值,因此您的地图不会生成学生列表。

    你想要这个:

    for student in students:
        student.age += 3
    

    这不会给你一个新列表,它会修改旧列表,但无论如何你的旧列表都会被修改,你没有做任何事情来产生新的学生。

    【讨论】:

    • 学究式地讲,Assignment 在 Python 中是一个操作。执行此操作的典型方法是使用语句。 ...而 lambdas 可以做作业,只是不能 assignment statements。 Poke 完全避免了这个问题(尽管他用那个不合时宜的额外空间创建了自己的一个)
    【解决方案4】:

    您可以使用setattr,它将更改应用于对象。一大优点是您可以继续使用相同的列表。

    map(lambda s: setattr(s, 'age', s.age + 3), students)
    

    来自文档:

    The function assigns the value to the attribute, provided the object allows it. For example, setattr(x, 'foobar', 123) is equivalent to x.foobar = 123.

    等价于:

    for s in students:
        s.age += 3
    

    如果您真的想要一个新列表:

    上述方法不会返回新列表;而是返回Nonesetattr 的返回值)。不过,添加 or 与数组中您想要的对象(在本例中为 s)的比较将修正这一点。

    new_students = map(lambda s: setattr(s, 'age', s.age + 3) or s, students)
    

    比较等价于None or s,它总是产生后者。另请注意,新列表与旧列表相同。

    【讨论】:

      【解决方案5】:

      您是否尝试过使用setattrgetattr 函数?有了这些就可以直接读写对象的可写属性了。

      所以你可以这样做:

      map_of_students = map(lambda student: setattr(student, "age", getattr(student, "age") + 3), students)
      
      print("Age BEFORE list: " + str(students[0].age)); list(map_of_students); print("Age AFTER list: " + str(students[0].age))
      

      在这种情况下,原始学生列表将使用每个学生的新年龄进行更新,这可能不一定是您想要的,但可以通过在投射地图之前备份原始列表的对象来轻松解决反对一个列表。在这种情况下,您可以这样做:

      students_bkp = []
      for s in students:
          students_bkp.append(Student(**s.__dict__))
      

      这是完整的sn-p:

      class Student(object):
          def __init__(self,name,age):
              self.name = name
              self.age = age
      
      student1 = Student('StudOne',18)
      student2 = Student('StudTwo',20)
      student3 = Student('StudThree',29)
      students = [student1,student2,student3]
      
      students_bkp = []
      for s in students:
          students_bkp.append(Student(**s.__dict__))
      
      map_of_students = map(lambda student: setattr(student, "age", getattr(student, "age") + 3), students)
      print("Age BEFORE list: " + str(students[0].age)); list(map_of_students); print("Age AFTER list: " + str(students[0].age)); print("Age BKP: " + str(students_bkp[0].age))
      

      【讨论】:

        【解决方案6】:

        问:“我可以使用 lambda 函数循环遍历类对象列表并更改属性的值吗?”

        答:是的....但你不应该。它的编码风格很差,效率低下,只适用于代码高尔夫之类的东西

        你应该像其他两个答案建议的那样写。

        ...但如果你真的想...

        new_list_of_students = [(lambda student:(setattr(student, 'age', student.age+3), 
        student))(s)[1] for s in students]
        
        print [student.age for student in new_list_of_students]
        

        打印:

        [21, 23, 32]
        

        ...甚至:

        from operator import itemgetter
        new_list_of_students = map(itemgetter(1),map(lambda student:(setattr(student, 
        'age', student.age+3), student),students))
        
        print [student.age for student in new_list_of_students]
        

        [相同的输出]

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-02-09
          • 1970-01-01
          • 1970-01-01
          • 2012-06-15
          • 2019-11-01
          • 1970-01-01
          • 1970-01-01
          • 2019-07-29
          相关资源
          最近更新 更多