2018/12/24

python 函數的可變參數 *args 和 **kwargs

一般函數的參數個數都是固定的,但如果遇到參數數量不固定的狀況,通常會將某些參數填上預設值,在 python function 可以支援兩種可變數量的參數 *args**kwargs

以下例子中的 fun 雖然定義了三個參數,但是後面兩個填上預設值,呼叫該函數時,就可以忽略 b 與 c,直接使用預設值。

def fun(a,b=2,c=3):
    print("a={}, b={}, c={}".format(a,b,c))

fun(1)

fun(1,22,33)

執行結果

a=1, b=2, c=3
a=1, b=22, c=33

*args是可變的positional arguments列表,**kwargs是可變的keyword arguments列表。兩個可以同時使用,但在使用時,*args必須在**kwargs的前面,因為positional arguments,有位置順序的對應,必須位於keyword arguments之前。

以下的例子,是定義 fun 有一個必要參數 a,以及可變的 *args

def fun(a, *args):
    print("a={}".format(a))
    for arg in args:
        print('Optional argument: {}'.format( arg ) )

fun(1,22,33)

執行結果為

a=1
Optional argument: 22
Optional argument: 33

如果同時加上 **kwargs

def fun(a, *args, **kwargs):
    print("a={}".format(a))
    for arg in args:
        print('Optional argument: {}'.format( arg ) )

    for k, v in kwargs.items():
        print('Optional kwargs argument key: {} value {}'.format(k, v))

fun(1,22,33, k1=44, k2=55)

執行結果為

a=1
Optional argument: 22
Optional argument: 33
Optional kwargs argument key: k1 value 44
Optional kwargs argument key: k2 value 55

除了在定義函數的部分,在呼叫函數時,也可以使用 *args**kwargs

print("")
args = [1,2,3,4]
fun(*args)

print("")
kwargs = {'k1':10, 'k2':11}
fun(1, **kwargs)

print("")
fun(1, *args, **kwargs)

執行結果為

a=1
Optional argument: 2
Optional argument: 3
Optional argument: 4

a=1
Optional kwargs argument key: k1 value 10
Optional kwargs argument key: k2 value 11

a=1
Optional argument: 1
Optional argument: 2
Optional argument: 3
Optional argument: 4
Optional kwargs argument key: k1 value 10
Optional kwargs argument key: k2 value 11

References

Python定義的函數(或調用)中參數args 和*kwargs的用法

[Python] 令新手驚呆的 **kwargs

PYTHON中如何使用ARGS和*KWARGS

理解 Python 中的 *args 和 **kwargs

2018/12/10

python 如何處理 json

json: JavaScript Object Notation 是 javascript 的子集合,是一種輕量級的資料交換格式,比 XML 簡單,也容易閱讀及編寫,處理過程分為序列化及反序列化兩個部分,分別是 Marshalling/Encoder 及 Unmarshalling/Decoder。

Marshalling/Encoder 就是將 python 的資料物件轉換為 json 字串,使用 json.dumps

Unmarshalling/Decoder 是將 json 字串轉換為 python 物件,使用 json.loads

使用 dumps, loads 的簡單範例

這是 python 的 dict 資料型別,因為 dict 沒有字串的表示方式,透過 repr 將 dict 轉換為可以列印的資料

json_obj = {
            'name' : 'Apple',
            'shares' : 100,
            'price' : 542.1
        }

logging.info( "json_obj type = "+str(type(json_obj))+ ", data =" + repr(json_obj) )

dict 可以透過 json.dumps 轉換為 json 字串

json_string = json.dumps(json_obj)

logging.info( "json_string type="+ str(type(json_string))+ ", data=" + json_string )

再透過 json.loads 將 json 字串轉換為 dict

json_object = json.loads(json_string)

        logging.info( "json_object type="+str(type(json_object))+", data="+repr(json_object) )

測試結果

json_obj type = <class 'dict'>, data ={'name': 'Apple', 'shares': 100, 'price': 542.1}

json_string type=<class 'str'>, data={"name": "Apple", "shares": 100, "price": 542.1}

json_object type=<class 'dict'>, data={'name': 'Apple', 'shares': 100, 'price': 542.1}

由 python 資料轉換為 json 的對照表為

python json
dict object
list, tuple array
str, unicode string
int, long, float number
True true
False false
None null

由 json 轉換為 python 資料的對照表為

json python
object dict
array list
string unicode
number(int) int,long
number(real) float
true True
false False
null None

pprint 及 dumps 的 indent, sort_keys 參數

如果 json 的字串比較長,列印到 console 時,可能會比較難查閱需要的資料,這時候有兩種方式可以處理

使用 pprint 可以直接用 pprint(json_obj)

from pprint imort pprint

json_obj = {
            'name' : 'Apple',
            'shares' : 100,
            'price' : 542.1
        }

logging.info( "json_obj type = "+str(type(json_obj))+ ", data =" + repr(json_obj) )

pprint(json_obj)

執行結果為

{'name': 'Apple', 'price': 542.1, 'shares': 100}

在 dumps 加上 indent 參數

json_string = json.dumps(json_obj, indent=4)

執行結果為

json_string type=<class 'str'>, data={
    "name": "Apple",
    "shares": 100,
    "price": 542.1
}

在 dumps 加上 sort_keys 可在轉換 json 時,同時進行 key 的排序

json.dumps({"c": 0, "b": 0, "a": 0}, sort_keys=True)

執行結果

{"a": 0, "b": 0, "c": 0}

loads 的 object_pairs_hookobject_hook 參數

通常 json 會轉換為 python 的 dict 及 list,可使用 object_pairs_hook,將 json 轉換為 OrderedDict

s = '{"name": "ACME", "shares": 50, "price": 490.1}'
        from collections import OrderedDict
data = json.loads(s, object_pairs_hook=OrderedDict)

logging.info( "json_object type="+str(type(data))+", data="+repr(data) )

執行結果

json_object type=<class 'collections.OrderedDict'>, data=OrderedDict([('name', 'ACME'), ('shares', 50), ('price', 490.1)])

也可以用 object_hook 將 json 轉換為 python 物件

class JSONObject:
    def __init__(self, d):
        self.__dict__ = d

data2 = json.loads(s, object_hook=JSONObject)
logging.info( "json_object type="+str(type(data2))+", name="+data2.name+", shares="+str(data2.shares)+", price="+str(data2.price) )

執行結果

json_object type=<class '__main__.JSONObject'>, name=ACME, shares=50, price=490.1

skipkeys

在 dumps 如果遇到 key 不是字串時,會發生 TypeError,可使用 skipkeys 略過無法處理的 key

data =  { 'b' : 789 , 'c' : 456 ,( 1 , 2 ): 123 }
print( json.dumps(data,skipkeys = True) )

執行結果

{"b": 789, "c": 456}

自訂 python 物件的 轉換函數

如果是自訂的 python 類別,通常是無法直接透過 dumps 序列化,會發生 error

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
p = Point(2, 3)

print(p)

json.dumps(p)

執行結果

TypeError: Object of type 'Point' is not JSON serializable

解決方式:

首先定義兩個獨立的 util function,object2dict 是將物件轉換為 dict,dict2object 則是將 dict 轉換為 object,在 dict2object 裡面會使用 dict 的 __module____class__ 這兩個資料,用來正確找到 class 的定義

def object2dict(obj):
    d = {
        '__class__': obj.__class__.__name__,
        '__module__': obj.__module__
    }
    # d.update( vars(obj) )
    d.update(obj.__dict__)
    return d


def dict2object(d):
    if '__class__' in d:
        module_name = d.pop( '__module__' )
        class_name = d.pop( '__class__' )
        logging.debug("module_name="+str(module_name)+", class_name="+class_name)

        # # from A import B
        import importlib
        objmodule = importlib.import_module(module_name)
        cls = getattr(objmodule, class_name)

        # objmodule = __import__(module_name)
        # cls = getattr(objmodule, class_name)

        # # use class directly
        # cls = classes[class_name]
        # # Make instance without calling __init__
        obj = cls.__new__(cls)
        for key, value in d.items():
            setattr(obj, key, value)
        return obj
    else:
        inst = d
    return inst

如果另外有一個類別定義在 test.point.py 裡面

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__( self ):
        return 'Point Object x : %d , y : %d' % ( self.x, self.y)

透過 object2dict 及 dict2object 就可以協助進行物件與 json 的轉換

from test import point
p = point.Point(2, 3)
logging.info(p)

json_str = json.dumps(p, default=object2dict)
logging.info(json_str)

o = json.loads(json_str, object_hook=dict2object)
logging.info( "json_object type="+str(type(o))+", data="+repr(o) )

執行結果

{"__class__": "Point", "__module__": "test.point", "x": 2, "y": 3}

module_name=test.point, class_name=Point
2018-09-03 16:01:00,274 INFO {95322,MainProcess} 

json_object type=<class 'test.point.Point'>, data=Point Object x : 2 , y : 3

有時會遇到 list of Point,這時需要修改 object2dict 讓他可以處理 obj 是 list 的狀況

def object2dict(obj):
    d = {}
    if isinstance(obj, list):
        return json.dumps(obj, default=object2dict)
    else:
        # d = {
        #   '__class__': obj.__class__.__name__,
        #   '__module__': obj.__module__
        # }
        d['__class__'] = obj.__class__.__name__
        d['__module__'] = obj.__module__
        # d.update( vars(obj) )
        d.update(obj.__dict__)
        return d

References

20.2. json — JSON encoder and decoder

Python處理JSON

6.2 讀寫JSON數據

Json概述以及python對json的相關操作

2018/12/03

一樣都是 PM,Project Manager 跟 Product Manager 有什麼不一樣

英文縮寫 PM 有兩種,一個是比較常聽到的 Project Manager,一個是 Product Manager,專業的 Product Manager 比較少見,大部分都是專案管理。另外還有人也將 Product Marketing 列入討論,不過這部分變成是 Product Manager 與 Marketing 的差異,管理跟行銷本質上就不一樣,這部分比較容易理解。

先看看不同的網路文章的意見:

一個 Project Manager 最大的任務就是將 Project 如期、如規格「正確」的執行,而這過程中,我想分為兩種層面的技能:技術與溝通。

技術層面上,PM 就是把業主/老闆心中想要的功能「翻譯」成工程師可以開發的規格、確認功能可行性、與業主/老闆確認規格、評估時間、排時程、確認優先順序、掌握開發進度、測試、驗收。溝通層面上,要去對專案所有關係人,進行不同協調,主要就是業主跟工程師之間的橋樑。

Product Manager 是以「使用者」出發、產品是否解決使用者的痛點、是否真正滿足使用者需求。你要討好的對象不是老闆、不是團隊成員、不是投資者、不是合作廠商,而是「使用者」。產品,就是一個隨時都有真實 user 在使用的東西。

所以做一個 Product Manager、或者待在一個做產品的團隊都可以深刻的感覺到,壞的情況是,當產品出問題或斷線,客服電話馬上進來、客服信箱馬上開始被罵;好的情況是,你也會收到使用者溫馨的回饋與感謝。

項目是局部優化工作。我們讓一組人為客戶創建(或改進已有的)功能,然後交給一個不知道為什麼變更、也不知道這個變更如何實現的團隊。變革的成本很高,需要花很長時間才能交到客戶手中。

圍繞產品(或服務)構建的組織有優勢,因為他們的組織結構可以監督整個工作過程,就是說,從想法到客戶,並基於客戶的反饋或反應想出新點子,形成閉環。我這樣說,是因為團隊為自己負責,對所提供的方案、交付質量、客戶體驗和滿意度有主人翁精神。進行看上去也許很小的變革,但是要允許想法快速地傳遞到客戶手中。

根據美國產品管理協會PDMA的定義,所謂的「產品管理(Product Management)」是指:在新產品開發的過程當中,通過不斷監控和調整市場組合的基本要素(其中包括產品及自身特色、溝通策略、配銷通路和價格),隨時確保產品或者服務能充分滿足客戶需求(Ensuring over time that a product or service profitably meets the needs of customers by continually monitoring and modifying the elements of the marketing mix, including: the product and its features, the communications strategy, distribution channels and price.)。而產品經理就是針對上述特定產品活動肩負所有責任的人。

Project Manager(專案經理):很重要,但通常是在食物鏈的底層 Product Marketing(產品行銷):很重要,但對於產品通常沒有實權 Product Manager(產品經理):通常在食物鏈頂層,但格局可大可小

行銷思維:盡可能讓對的受眾,精準接收到我想傳達的資訊,並做出行動。 專案管理思維:資源與風險的管理與控制,多方平衡。 產品思維:了解顧客的痛點,提出解決方法。 營運思維:流程觀與協作思維,讓各個部門順暢協作。

企業在新產品開發過程中,經常遭遇許多的瓶頸與風險,使得開發專案經常發生延遲、失敗的問題。歸咎其因,除了缺乏一套完整的新產品開發流程做好專案管理掌控之外,更缺少一位可以來整合各部門資源,掌控開發專案的品質、成本與時程,以增進產品競爭力的產品經理。

根據Wheelwright and Clark(1992)在《Creating Project Plans to Focus Product Development》的研究報告指出:企業的新產品開發專案分為以下四種類型:

1.衍生產品專案(Derivative Projects)

局部創新與改進的產品專案,包括對現有產品改良以提升功能、降低成本,或為滿足不同區隔市場客戶的需求而改變功能外型。開發過程的風險較低,專案時程較短,所需資源也比較有限。

2.突破性產品專案(Breakthrough Projects)

突破性產品專案與企業當前主流產品開發專案有很大差異,資源投入的需求量高,開發風險與不確定性很高,但短期間很難產生成果與利潤,因此開發過程中遭遇的阻力就比較大。管理的複雜度遠要高過於其他三種專案類型。

3.平台產品專案(Platform Projects)

平台產品的改變幅度遠高過於衍生產品,但卻不像突破性產品使用從未發展過的新技術與新材料。平台產品比較著重於系統上的創新,衍生產品或許只改變產品中的一項特質(如:成本、品質、產品表現等),而平台產品則針對全面的產品問題進行改善。

4.研究發展專案(R&D Projects)

研究發展專案不屬於商業化應用的範疇,但因為它是上述三種產品開發專案的先驅活動,而且也會佔用相當比例的資源。


Product 與 Project 的差異在於生命週期 以及 時程,任何 Product 產品/商品,其生命週期會比 Project 長,因為身為 Product,其概念就是企業內固定在銷售的一個商品項目,而 Product 開發本身,可以切割為多個 project。

在 美國專案管理學會 Project Management Institute PMI 的定義中,專案是指一項暫時性的任務、配置,以開創某獨特性的產品或服務。專案有起迄時間。專案團隊並不是一直存在的,而是為了某一特定目的或產品組成的。

『專案管理』是將管理知識、技術、工具、方法綜合運用到任何一個專案之上,使專案得以符合要求。 』專案經過啟動、規劃、執行、監控、結案這五大流程,而專案經理人則運用專案管理的九大知識對專案進行管理,讓專案可以如期、符合品質要求並在成本內完成。

簡單來說,Product 每一次版本的功能與規格制定,到開發最後測試完成,這樣一個週期,完成了一個版本,則本產品的本次版本的專案就已經結束。

換句話說,專案是一時的,產品則會存在很久。

因為生命週期不同,產品管理者需要承擔的責任會比較久,至於專案管理的部分,基本上只需要在專案結束時,能夠順利結案就可以了。專案管理者要承擔的責任,就是產品本身所有相關的事情,包含產品要解決的問題,解決的方法,開發的過程,版本功能的分割,除錯,到後續的問題處理,還有維護,甚至是產品的行銷,所有的面向都要承擔責任。

至於重要性,個人認為是都很重要,遇到的問題不同,也各有各的難處。更重要的是要找到正確的負責人,制度問題可以靠人去調整及解決,但是人如果錯了,就很難彌補問題。