Python3中`sorted()函數`與`lambda表達式`原理解析

Python3中sorted()函數lambda表達式原理解析

相信很多小夥伴們在一開始學習Python的一些高級用法時遇到過很多困擾。
我準備日常分享一些比較淺顯的原理解析幫助大家理解。

博主的原文:


問題描述

很多小夥伴面對這樣的排序,很是懵逼:

# 預按照每個字典中鍵'b'的值進行列表排序
L = [{'a': 1, 'b': 4}, {'a': 1111, 'b': 2}, {'a': 1111, 'b': 3}]
L_sorted = sorted(L, key=lambda d: d['b'], reverse=False)
print(L_sorted)

運行結果:

[{'a': 1111, 'b': 2}, {'a': 1111, 'b': 3}, {'a': 1, 'b': 4}]

這裏麪包含了函數高級用法中的sorted和lambda的兩個用法

下面,我將逐一解析。

原理解析

  • lambda表達式相當於一個簡化的函數
  • sorted(iterable, key=None, reverse=False),此處的key=需要傳遞一個函數,相當於將iterable中的每一個元素迭代取出,傳入key=處給定的函數,通過函數處理返回的值組成的新的迭代對象,通過該新對象的排序映射回iterable完成排序。

示例1:lambda表達式理解:

# 1) 普通函數func_add的定義
def func_add(x, y):
    return x + y
print(func_add(10, 20))  # 30

# 2) 對於函數func_add, 可以使用lambda表達式一行定義
myadd = lambda x, y: x + y
print(myadd(10, 20))  # 30

示例2: sorted函數理解

# sorted原理解析 —— 自定義函數實現sorted
def my_sorted(iters, key=None, reverse=None):
    if key:
        DICT = dict()
        for it in iters:
            # 用函數處理過後的值做鍵, 處理相同的鍵加入列表
            DICT.setdefault(key(it), []).append(it)
        INDEX = list(DICT)  # 獲得函數處理過後的值列表
        INDEX.sort(reverse=reverse)  # 排序
        # 按順序展開
        return [xx for x in INDEX for xx in DICT[x]]
    else:
        INDEX = list(iters)
        INDEX.sort(reversed=reverse)
        return INDEX


names = ['Tom', 'Spike', 'Jerry', 'Tyke']

L = sorted(names, key=len, reverse=True)
print(L)  # 結果 ['Tom', 'Tyke', 'Jerry', 'Spike']

L = my_sorted(names, key=len, reverse=True)
print(L)  # 結果 ['Tom', 'Tyke', 'Jerry', 'Spike']

附:原文基礎知識

lambda表達式

(原文鏈接: lambda表達式

引入:

  • 除了def語句可以創建函數之外,lambda表達式也可以創建函數。

概念:

  • lambda表達式(又稱匿名函數),用於封裝有限的邏輯的函數
  • lambda的主體是一個表達式,而不是一個代碼塊。僅僅能在lambda表達式中封裝有限的邏輯進去。

語法格式:

  • lambda [變量1,變量2,… ]:表達式

說明:

  • 可以省略變量
  • 只能包含一條表達式

示例:lambda函數理解

# 1) 普通函數func_add的定義
def func_add(x, y):
    return x + y
print(func_add(10, 20))  # 30

# 2) 對於函數func_add, 可以使用lambda表達式一行定義
myadd = lambda x, y: x + y
print(myadd(10, 20))  # 30

# 3) 語法理解 x 爲形參接收值, 後面緊跟表達式返回值
myfunc = lambda x: 123 + x
print(myfunc(100))  # 223

# lambda可以行使正常函數的所有功能
myfunc = lambda: print('hello', end=' ')
print(myfunc())  # hello None

# 求1到10的平方和
fun = lambda x: sum([x * x for x in range(1, x + 1)])
print(fun(10))  # 385

sorted 函數

(原文鏈接: sorted()函數

  • 作用:

    • 將原可迭代對象提供的數據進行排序,生成排序後的列表
  • 格式:

    • sorted(iterable, key=None, reverse=False)
    • 說明:
      • iterable 可迭代對象
      • key 函數是用來提供一個排序參考值的函數,這個函數的返回值將作爲排序的依據
      • reverse 標誌用來設置是否降序排序
    • 返回
      • 列表list
  • 理解:

    • 此處的key=需要傳遞一個函數,相當於將iterable中的每一個元素迭代取出,傳入key=處給定的函數,通過函數處理返回的值組成的新的迭代對象,通過該新對象的排序映射回iterable完成排序。
  • 與列表中自帶方法 L.sort() 有什麼區別?

    • sorted(L)
      • 是Building域名空間的一個系統函數;
      • 是將L作爲參數輸入,返回新列表,對原列表無影響;
    • L.sort()
      • 是list類中的一個方法,只能痛毆;
      • 是將L的內部元素進行直接排列,不返回新對象
    • 示例:
      L = [3, 5, 4, 2, 1]
      L2 = sorted(L)
      print("L:", L, "L2:", L2)
      # L: [3, 5, 4, 2, 1] L2: [1, 2, 3, 4, 5]
      a = L.sort()
      print("L:", L, "a:", a)
      # L: [1, 2, 3, 4, 5] a: None
      

示例1: sorted的簡單示例

L = [5, -2, -4, 0, 3, 1]
print(sorted(L))  # 直接按大小排序
# [-4, -2, 0, 1, 3, 5]
print(sorted(L, key=abs))  # 按絕對值排序
# [0, 1, -2, 3, -4, 5]
print(sorted(L, key=abs, reverse=True)  # 按絕對值降序排序
# [5, -4, 3, -2, 1, 0]

示例2: sorted原理解析 —— 自定義函數實現sorted

# sorted原理解析 —— 自定義函數實現sorted
def my_sorted(iters, key=None, reverse=None):
    if key:
        DICT = dict()
        for it in iters:
            # 用函數處理過後的值做鍵, 處理相同的鍵加入列表
            DICT.setdefault(key(it), []).append(it)
        INDEX = list(DICT)  # 獲得函數處理過後的值列表
        INDEX.sort(reverse=reverse)  # 排序
        # 按順序展開
        return [xx for x in INDEX for xx in DICT[x]]
    else:
        INDEX = list(iters)
        INDEX.sort(reversed=reverse)
        return INDEX


names = ['Tom', 'Spike', 'Jerry', 'Tyke']

L = sorted(names, key=len, reverse=True)
print(L)  # 結果 ['Tom', 'Tyke', 'Jerry', 'Spike']

L = my_sorted(names, key=len, reverse=True)
print(L)  # 結果 ['Tom', 'Tyke', 'Jerry', 'Spike']

高級示例:

  • lambdasorted高級聯用
  1. 對列表裏的字典排序
L = [{'a': 1, 'b': 4}, {'a': 1111, 'b': 2}, {'a': 1111, 'b': 3}]
L_sorted = sorted(L, key=lambda d: d['b'], reverse=False)
# [{'a': 1111, 'b': 2}, {'a': 1111, 'b': 3}, {'a': 1, 'b': 4}]
  1. 對字典進行按key排序
d = {'a':25, 'c':27, 'b':20, 'd':22}
L_sorted = sorted(d.items(), key=lambda x:x[0])
print(L_sorted)
# [('a', 25), ('b', 20), ('c', 27), ('d', 22)]
  1. 對字典進行按values排序
d = {'a':25, 'c':27, 'b':20, 'd':22}
L_sorted = sorted(d.items(), key=lambda x:x[1])
print(L_sorted)
# [('b', 20), ('d', 22), ('a', 25), ('c', 27)]
  1. 降序排序的另類寫法
  • 問題一:如何對字母進行數學上寫法的降序排序?
    • 利用ord()函數轉爲編碼值,再*-1
Data = [['y', 2], ['x', 3], ['z', 4], ['a',1]]

# 對每個列表的第一個值進行正常排序
print(sorted(Data, key=lambda x: x[0]))
# [['a', 1], ['x', 3], ['y', 2], ['z', 4]]
# 對每個列表的第一個值進行降序排序
print(sorted(Data, key=lambda x: x[0], reverse=True))
# [['z', 4], ['y', 2], ['x', 3], ['a', 1]]
print(sorted(Data, key=lambda x: ord(x[0])*-1))  # 問題一
# [['z', 4], ['y', 2], ['x', 3], ['a', 1]]

# 對每個列表的第二個值進行正常排序
print(sorted(Data, key=lambda x: x[1]))
# [['a', 1], ['y', 2], ['x', 3], ['z', 4]]
# 對每個列表的第二個值進行降序排序
print(sorted(Data, key=lambda x: x[1], reverse=True))
# [['z', 4], ['x', 3], ['y', 2], ['a', 1]]
print(sorted(Data, key=lambda x: x[1]*-1))
# [['z', 4], ['x', 3], ['y', 2], ['a', 1]]
  1. 多重排序
  • 難點:
    • 如何實現部分列的升序和部分列的降序排序同時存在?
# - 排序規則
#   - 先按照書籍編號降序排序
#   - 再按照書名正序排序
#   - 再按照年份降序排序

# 書籍的編號
number = {
    "Robinson Crusoe": "B",
    "The Old Man and the Sea": "A",
    "The Little Prince": "C",
    "Secret garden": "A",
    "Thorn bird": "A"
}

# 年份,書籍,銷售額
data_text = """2017,126,Robinson Crusoe
2017,110,The Old Man and the Sea
2017,152,The Little Prince
2017,98,Secret garden
2017,89,Thorn bird
2018,116,Robinson Crusoe
2018,98,The Old Man and the Sea
2018,176,The Little Prince
2018,79,Secret garden
2018,90,Thorn bird
2019,122,Robinson Crusoe
2019,102,The Old Man and the Sea
2019,187,The Little Prince
2019,102,Secret garden
2019,103,Thorn bird"""

Datas = [x.split(',') for x in data_text.splitlines()]
# 對數據進行排序
result = sorted(Datas, key=lambda x: (ord(number[x[2]])*-1,
                                      x[2], int(x[0])*-1))
# 結果的打印
for x in result:
    print(number[x[2]], *x, sep='\t')

運行結果:

C       2019    187     The Little Prince
C       2018    176     The Little Prince
C       2017    152     The Little Prince
B       2019    122     Robinson Crusoe
B       2018    116     Robinson Crusoe
B       2017    126     Robinson Crusoe
A       2019    102     Secret garden
A       2018    79      Secret garden
A       2017    98      Secret garden
A       2019    102     The Old Man and the Sea
A       2018    98      The Old Man and the Sea
A       2017    110     The Old Man and the Sea
A       2019    103     Thorn bird
A       2018    90      Thorn bird
A       2017    89      Thorn bird
  • itemgetter與attrgetter模塊的使用
    • 可以簡化代碼
from operator import itemgetter

student_tuples = [
    ('john', 'A', 15),
    ('jane', 'B', 12),
    ('dave', 'B', 10),
]

# 按照索引爲2的數據排序
print(sorted(student_tuples, key=itemgetter(2)))
# [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]

# 多重排序:按照索引爲1,再按照2的數據排序
print(sorted(student_tuples, key=itemgetter(1, 2)))
# [('john', 'A', 15), ('dave', 'B', 10), ('jane', 'B', 12)]
from operator import attrgetter

 class Student:
        def __init__(self, name, grade, age):
                self.name = name
                self.grade = grade
                self.age = age
        def __repr__(self):
                return repr((self.name, self.grade, self.age))

student_objects = [
        Student('john', 'A', 15),
        Student('jane', 'B', 12),
        Student('dave', 'B', 10),
]

# 按age排序
print(sorted(student_objects, key=attrgetter('age')))
# [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]

# 多重排序,先以grade,然後再以age來排序
print(sorted(student_objects, key=attrgetter('grade', 'age')))
[('john', 'A', 15), ('dave', 'B', 10), ('jane', 'B', 12)]

練習:

  • 已知:
    • names = ['Tom', 'Jerry', 'Spike', 'Tyke']
  • 排序的依據爲原字符串反序的字符串
    • 'moT', 'yrreJ', 'ekipS', 'ekyT'
  • 結果:
    • ['Spike', 'Tyke', 'Tom', 'Jerry']

參考:

>>> sorted(['Tom', 'Jerry', 'Spike', 'Tyke'],
           key=lambda x:x[::-1])
['Spike', 'Tyke', 'Tom', 'Jerry']
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章