2017年4月26日 星期三

Python - 使用*args和**kwargs解決找min和max問題(進階版,傳入值不一定為數字)

本次的題目是此網址

https://py.checkio.org/mission/min-max/





主要解決幾件事情

  • 比大小,比max傳入max function;比min傳入min function
  • 傳入的值不一定為數字型態(int or float),有可能是string or list等型態,因此可能需要判斷傳入值的種類
  • (非必要的變數)除此之外,使用者還可以傳入特定的算法或改變型態,例如傳入的float型態,要求轉成int之後再做比較


所以這題一開始就有兩個function
但傳入的值因為不固定有幾個,可能好幾個int,或者只傳入一個list或tuple的int

因此在function上傳值的方式寫成

#def min(*args, **kwargs):


先用懂這兩個傳值意義才能開始解題吧
在這兩個傳值的解釋中,我參考了以下幾個網站:
http://kodango.com/variable-arguments-in-python

http://stackoverflow.com/questions/3394835/args-and-kwargs

http://agiliq.com/blog/2012/06/understanding-args-and-kwargs/

我對這樣的傳入值的基本理解是:

  • '*'代表的意思:*是代表解壓縮(unpack)list/tuple,在第三個網站有一個非常棒的解釋,當傳入的值為list/tuple時,他會unpack list/tuple到對應的接收變數上。


def fun(a,b,c):
    print (a,b,c)

fun(1,2,3)  #output = 1,2,3
l=[1,2,3]
fun(*l)   #output = 1,2,3

  • *args 是當不知道傳入值的型態以及多少時使用,傳入會變成tuple型態,所以假設只傳入一個list就存在args[0]的位置;假設function的型態為下,可以知道args會把剩下的值接收並包進來做成tuple;如果只傳入一個變數,前面的變數a接收對應變數後,args就只會回傳一個空tuple:

def fun1 (a, *args):
    print ("a is "+ str(a))
    print ("args is "+ str(args))

fun1(1,2,3,4) 
#output:
#a is 1
#args is (2, 3, 4)

fun1(1)
#output:
#a is 1
#args is ()

  • '**'代表的意思:**是解壓縮(unpack) dictionary,當傳入dictionary時,會把字典對應的變數傳入,**的字典名稱必須在function的變數中有這個名字,否則會出錯

def fun2(a,b,c):
    print (a,b,c)

d={'c':3} 
e={'k':4}


fun2(1,2,**d)
#output = 1,2,3

fun2(1,2,**e)
#output: TypeError: fun2() got an unexpected keyword argument 'k'
#因為fun2沒有k這個變數,所以出錯

  • **kwargs是接收並轉換成dictionary的型態。
    如果要印出字典對應的值,可以使用kwargs['dic'],會印出對應的key;而且接收只能像範例一樣,如果要傳入字典的話也要加**才不會出錯。

def fun3(a, **kwargs):
    print (a)
    print (kwargs)
    print (kwargs['b']) 
    #假設字典裡沒有傳入b的變數,則會出錯
    key = kwargs.get("b", None) 
    #如果dictionary裡有'b',則key=b對應的值;如果沒有則key=None

fun3(1, b=2, c=3)
#output:
#1
#{'b': 2, 'c': 3}
#2
#2

fun3(1, {'b':2, 'c':3}) 
#error 

fun3(1, **{'b':2, 'c':3}) 
#output:
#1
#{'b': 2, 'c': 3}
#2
#2

  • *args和**kwargs合用在function時,*args一定要擺前面而**kwargs要擺後面

以上知道傳入的變數如何操作以後
就可以繼續解決這題目了

第一個判斷:是否有key值,有key必須用key判斷
因此要使用get的方式抓取

key = kwargs.get("key", None)

有key走key的判斷,None走一般的比大小


第二個判斷:是傳入一個像list類型的複合值,或者是多值
因此先確定args傳入的長度

len(args)

假設長度為一,則傳入的應該是像list /tuple/string等值
作其他的處理

假設長度不為一,應該是傳入多組數字
直接做比對

第三個判斷-a:傳入多組數字的核心判斷式
將tuple轉成list
用list[0]的值做為 min(max) 初始值
跑迴圈的方式修改min(max)值,最後回傳最終值

核心判斷式如下:

#最小值判斷
min = args[0]
for select in args:
    if min > select:
        min = select
print (min)
return min

#最大值判斷
max = args[0]
for select in args:
    if max < select:
        max = select
print (max)
return max

第三個判斷-b:傳入非數字值時的比對的核心判斷

在python裡,用 變數.__class__ 確認是何種型態

舉例:傳入的值是string,則判斷式為

if args[0].__class__ is str:

這裏我用另一個變數把args[0]的東西撈出來,比較方便接下來的使用
範例上看到只要是string形式的就是比ASCII碼的大小

轉ASCII碼的function我是查找這篇


ASCII 轉 int
ord('字母')  = 對應int

int 轉 ASCII
chr(數字) = 對應ASCII碼

會轉碼後
再使用 第三個判斷-a的技巧,就可以比較大小值

if args[0].__class__ is str:
    k = str(args[0])
    min = k[0]
    for select in k:
        if int(ord(min)) > int(ord(select)):
        min = select
    print (min)
    return min


第四個判斷:有key的使用

由於把key用get的方式拿出來
只要在第三個判斷式-a時將key值包住,用給key值得方式判斷

min = args[0]
for select in args:
    if key(min) > key(select):
        min = select
print (min)
return min


綜合以上,我們可以慢慢的把程式碼給完成
雖然相比其他答案算是頗冗長,好像並不需要判斷傳入何種值即可比出大小
只需要判斷傳入值是否多值或複合值即可
(但礙於是別人的答案,因此就不貼上來了)

後面做check時還有多種type的型態
因此最後還多了好幾種不同的複合值型態判斷

但不管如何,總算是成功過關了!

以下是最終的程式碼


def min(*args, **kwargs):
    key = kwargs.get("key", None)
    print ("key is"+str(key))
    if key == None:
        if len(args) is 1:
            print (args[0].__class__)
            if args[0].__class__ is list or args[0].__class__ is tuple or args[0].__class__ is range:
                k = list(args[0])
                min = k[0]
                for select in k:
                    if min > select:
                        min = select
                print (min)
                return min
            if args[0].__class__ is str:
                k = str(args[0])
                min = k[0]
                for select in k:
                    if int(ord(min)) > int(ord(select)):
                        min = select
                print (min)
                return min
            else:
                k = list(args[0])
                #print (k[0])
                min = k[0]
                for select in k:
                    if min > select:
                        min = select
                print (min)
                return min
        else:
            min = args[0]
            for select in args:
                if min > select:
                    min = select
            print (min)
            return min
    else:
        if len(args) is 1:
            k = args[0]
            min = k[0]
            for select in k:
                if key(min) > key(select):
                    min = select
            print (min)
            return min
        else:
            min = args[0]
            for select in args:
                if key(min) > key(select):
                    min = select
            print (min)
            return min
    return None


def max(*args, **kwargs):
    key = kwargs.get("key", None)
    print ("key is"+str(key))
    if key == None:
        if len(args) is 1:
            if args[0].__class__ is list or args[0].__class__ is tuple or args[0].__class__ is range:
                k = list(args[0])
                max = k[0]
                for select in k:
                    if max < select:
                        max = select
                print (max)
                return max
            if args[0].__class__ is str:
                k = str(args[0])
                max = k[0]
                for select in k:
                    if int(ord(max)) < int(ord(select)):
                        max = select
                print (max)
                return max
            else:
                k = list(args[0])
                #print (k[0])
                max = k[0]
                for select in k:
                    if max < select:
                        max = select
                print (max)
                return max
        else:
            max = args[0]
            for select in args:
                if max < select:
                    max = select
            print (max)
            return max
    else:
        if len(args) is 1:
            k = list(args[0])
            max = k[0]
            for select in k:
                if key(max) < key(select):
                    max = select
            print (max)
            return max
        else:
            max = args[0]
            for select in args:
                if key(max) < key(select):
                    max = select
            print (max)
            return max
    return None

沒有留言:

張貼留言