python函數參數的pack與unpack


python函數參數的pack與unpack

上周在使用django做開發的時候用到了mixin(關於mixin我還要寫一個博客專門討論一下,現在請參見這里),其中又涉及到了一個關於函數參數打包(pack)的問題,導致延誤了開發時間,所以在這里記錄一下,稍后會說到具體的背景。

背景交代:

具體情景是這樣的,我需要一個view可以在查詢的同時可以分頁,又可以在返回的 queryset 上做更多的查詢操作。為了解決這個問題,我自己寫了一個mixiin :

class  MultipleOjbectQueryPageMixin(object):
	'''
	query_params = [['filter , 'tags__label__contains, 'wow' ],[],[]]
	'''
	query_params = None
	paginate_by = None
	page_size_kwargs = 'size'
	
	#新加入的方法
	def get_paginate_by(self, queryset):
		
		if self.page_size_kwargs in self.request.kwargs:
			self.paginate_by = int(self.request.kwargs[self.page_size_kwargs])
		
		return self.paginate_by
		
	def do_query(self, queryset):
	
		if self.query_params:
			iterator = iter(self.query_params)
			
			try:
				param = iterator.next()
				try:
					if params[1] is not None and params[0] is not None:
						#函數參數解包的問題
						queryset = getattr(queryset, params[0])(**{params[1] : params[2]})			
						return queryset
					else:
						queryset = getattr(queryset,params[0])()
						return queryset
				except AttributeError as e:
					print e
					
			except  StopIteration as e:
				print e
				
		else:
			return queryset.all()				
	#新加入的方法
	def get_queryset(self):
		
		if self.queryset is not None:
			queryset = self.queryset
			if isinstance(queryset, QuerySet):
				queryset = self.do_query(queryset)
		elif self.model is not None:
			queryset = self.do_query(self.model._default_manager)
		else:
			raise ImproperlyConfigured(
				"%(cls)s is missing a QuerySet. Define"
				"%(cls)s.model, %(cls)s.queryset, or override"
				"%(cls)s.get_queryset()." % {
					'cls' : self.__class__.__name__
				}
			)
		ordering = self.get_ordering()
		if ordering:
			if isinstance(ordering, six.string_types):
				ordering = (ordering, )
			queryset = queryset.order_by(*ordering)
		
		return queryset

這個 mixin只自定義了2個方法,其中一個是從url中獲取當前分頁的頁面大小,也就是page_size, 在MultipleObjectMixin類中這這個參數的名字叫做 paginate_by ,在 django.views.generic.ListView 中直接繼承了這個mixin , 不過ListView只有基本的分頁功能 (你可以直接在url中傳入/?page=1,來進行分頁,mixin中都寫好了),並沒有可以從url中獲取頁面大小,我增加的方法可以直接從url中獲取要分頁的頁面大小,然后傳入page參數就可以完成分頁。

另外一個方法比較復雜,它擁有了基本的查詢功能,我的初衷是要從url中得到博客的標簽信息,然后根據標簽查詢到相應標簽下的博客,所以就寫了一個方法,這個方法由 get_queryset(self) 這個方法來調用, 這樣就可以比較方便的在已有代碼的基礎上去做查詢,實際使用中我寫了一個功能類似於BaseListView類的View類來繼承該mixin, 然后重寫自己的get方法就可以了,在myapp.views中使用起來非常方便,直接繼承自己寫的view類,然后在類中定義需要的屬性,就可以了,myapp.views中沒有方法,看起來非常整潔,我認為這就是CBV的好處了。

如果你要說我直接自己寫get方法,直接在request中獲取tag的值,然后

pk = request.kwargs['id']
tag = Tag.objects.get(pk = pk)
blogs = tag.blogs.all()

那么我會說這完全是可以的,但是你以后需要再查詢別的東西的時候,你必須一直寫get方法,這樣的寫法,會導致CBV的優勢盪然無存,那還不如直接去寫FBV的好,大家都說FBV容易理解,其實我覺得讓我寫的話肯定是寫CBV,不喜歡看到亂糟糟的代碼。

函數參數的解包問題:

由於已經很久很久沒有寫python了,所以不免會忘記一些東西,這次跳的一個小坑就是在函數參數上出了問題。
我想實現的是這樣的效果

params = { 'tags__label__conatains' : wow'}
Blog.objects.filter(params)
上面這樣直接傳入字典是不行的
因為filter中的參數是一個有默認值的tags__table__contains 參數,
我的目的是想給它一個值,這樣在func(*args, **kwargs)里是解析不到kwargs里的,實際上傳入一個字典的結果都在args 參數里,因為我們傳入的是一個對象,而不是kv, 只有傳入test(a=1, b=2)這樣的值才會被解析到kwargs中,現在我們這樣寫
Blog.objects.filter(params.keys()[0] = params.values()[0])
還是不行
接着我們這樣寫
k = params.keys()[0]
v = params.values()[0]
Blog.objects.filter( k = v)
你會發現這樣寫可以,但是我們需要的參數變成k了,而不是 tags__label__contains
所以這樣也是不行的。
然后我們又換了一種方法
Blog.objects.Filter({ k : v})
結果這個參數又跑到args里去了,
最后正確的寫法是這樣的
Blog.objects.Filter(**{k:v})
這樣先解包就可以把帶變量的參數傳到kwargs

對於args來說是一樣的
def test(*args, **kwargs):
	print args
	print kwargs
	
s = (1,2,3,4,)
test(s)
輸出:
((1,2,3,4),)
很明顯系統把s這個元組當成了一個對象,如果你打算傳入之后對,args進行遍歷操作話,會發現args里只有一個對象,但是我明明傳入了一個有4個元素的元組啊。
正確的寫法是:
test(*s)
輸出為:
(1,2,3,4)
這個時候你就可以去遍歷了,絕對沒問題了


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM