《编写高质量代码 改善python程序的91个建议》读书笔记 之四 深浅cpoy和counter

38、使用cpoy模块深拷贝对象

浅拷贝(shallow copy):构造一个新的复合对象,并将从原对象中发现的引用插入新对象中。
深拷贝(deep copy):构造一个新的复合对象,但遇到引用时,会继续递归拷贝其指向的具体内容,也就是说,他会针对引用所指向的对象继续拷贝,因此产生的对象不受其它引用对象操作的影响。
 深拷贝需要依赖copy模块中的deepcopy函数。
 在包含引用的数据结构中,浅拷贝“仅仅拷贝了引用,而不是引用所指向的具体内容”,深拷贝不仅拷贝引用,也拷贝引用所指向的对象,深拷贝得到的对象和原对象是完全相互独立的。

 参考以下代码:

import copy

#定义Pizza类
class Pizza(object):
	def __init__(self, name, size, price):
		self.name = name
		self.size = size
		self.price = price

	def get_pizza_info(self):
		#获取pizza相关信息
		return self.name, self.size, self.price

	def show_pizza_info(self):
		#显示pizza信息
		print ('Pizza name: {}'.format(self.name))
		print ('Pizza size: {}'.format(self.size))
		print ('Pizza price: {}'.format(self.price))

	def change_size(self, size):
		self.size = size

	def change_price(self, price):
		self.price = price

#定义订单类
class Order(object):
	def __init__(self, name):
		self.customer_name = name
		self.pizza_list = []
		self.pizza_list.append(Pizza('Mushroom', 12, 30))

	def order_more(self, pizza):
		self.pizza_list.append(pizza)

	def change_name(self, name):
		self.customer_name = name

	def get_order_detail(self):
		print ("customer name: " + self.customer_name)
		for pizza in self.pizza_list:
			pizza.show_pizza_info()

	def get_pizza(self, number):
		return self.pizza_list[number]

#客户1的订单
customer1 = Order('Zhang')
customer1.order_more(Pizza('seafood', 9, 40))
customer1.order_more(Pizza('fruit', 12, 35))

print ("customer1 order information: ")
customer1.get_order_detail()
print ('-' * 30)

#客户2的订单
#先用浅拷贝
customer2 = copy.copy(customer1)

print ("order 2 customer name: {}".format(customer2.customer_name))
customer2.change_name('Li')
customer2.get_pizza(2).change_size(9)
customer2.get_pizza(2).change_price(30)

print ('customer2 order information: ')
customer2.get_order_detail()
print ('-' * 30)

#再回头检查客户1的订单信息
print ("custome1 order information: ")
customer1.get_order_detail()

 浅拷贝输出如下:

customer1 order information: 
customer name: Zhang
Pizza name: Mushroom
Pizza size: 12
Pizza price: 30
Pizza name: seafood
Pizza size: 9
Pizza price: 40
Pizza name: fruit
Pizza size: 12
Pizza price: 35
------------------------------
order 2 customer name: Zhang
customer2 order information: 
customer name: Li
Pizza name: Mushroom
Pizza size: 12
Pizza price: 30
Pizza name: seafood
Pizza size: 9
Pizza price: 40
Pizza name: fruit
Pizza size: 9
Pizza price: 30
------------------------------
custome1 order information: 
customer name: Zhang
Pizza name: Mushroom
Pizza size: 12
Pizza price: 30
Pizza name: seafood
Pizza size: 9
Pizza price: 40
Pizza name: fruit
Pizza size: 9
Pizza price: 30

 改用深拷贝:

#deep copy
customer2 = copy.deepcopy(customer1)

 输出结果如下:

customer1 order information: 
customer name: Zhang
Pizza name: Mushroom
Pizza size: 12
Pizza price: 30
Pizza name: seafood
Pizza size: 9
Pizza price: 40
Pizza name: fruit
Pizza size: 12
Pizza price: 35
------------------------------
order 2 customer name: Zhang
customer2 order information: 
customer name: Li
Pizza name: Mushroom
Pizza size: 12
Pizza price: 30
Pizza name: seafood
Pizza size: 9
Pizza price: 40
Pizza name: fruit
Pizza size: 9
Pizza price: 30
------------------------------
custome1 order information: 
customer name: Zhang
Pizza name: Mushroom
Pizza size: 12
Pizza price: 30
Pizza name: seafood
Pizza size: 9
Pizza price: 40
Pizza name: fruit
Pizza size: 12
Pizza price: 35

39、使用Counter进行计数

 首先介绍三种传统计数方法:

  • 使用dict
some_data = ['a', '2', 2, 4, 5, '2', 'b', 4, 7, 'a', 5, 'd', 'a', 'z']
count_frq = dict()

for item in some_data:
	if item in count_frq:
		count_frq[item] += 1
	else:
		count_frq[item] = 1

print (count_frq)

#输出结果如下
'a': 3, '2': 2, 2: 1, 4: 2, 5: 2, 'b': 1, 7: 1, 'd': 1, 'z': 1}
  • 使用defaultdict
from collections import defaultdict

some_data = ['a', '2', 2, 4, 5, '2', 'b', 4, 7, 'a', 5, 'd', 'a', 'z']
count_frq = defaultdict(int)

for item in some_data:
	count_frq[item] += 1

print (count_frq)

#输出结果如下
defaultdict(<class 'int'>, {'a': 3, '2': 2, 2: 1, 4: 2, 5: 2, 'b': 1, 7: 1, 'd': 1, 'z': 1})
  • 使用set和list
some_data = ['a', '2', 2, 4, 5, '2', 'b', 4, 7, 'a', 5, 'd', 'a', 'z']

count_set = set(some_data)
count_list = []

for item in count_set:
	count_list.append((item, some_data.count(item)))

print (count_list)

#输出结果如下
[('b', 1), (2, 1), (4, 2), (5, 2), (7, 1), ('2', 2), ('z', 1), ('d', 1), ('a', 3)]
  • 更优雅的更pythonic的方法:使用colletcions.Counter函数
from collections import Counter

some_data = ['a', '2', 2, 4, 5, '2', 'b', 4, 7, 'a', 5, 'd', 'a', 'z']
print (Counter(some_data))

#输出结果如下
Counter({'a': 3, '2': 2, 4: 2, 5: 2, 2: 1, 'b': 1, 7: 1, 'd': 1, 'z': 1})

 划重点:相关知识介绍


  • set()函数
     set函数创建一个无序不重复的集合,参数为可迭代对象,返回一个新的集合对象。
setname = set(iteration)

 添加有两种方法:add()和update(),add是整个添加,update是拆分添加

>>> a = set('Wheel')
>>> a
set(['h', 'e', 'l', 'W'])
>>> a.add('Witch')
>>> a
set(['h', 'Witch', 'e', 'l', 'W'])
>>> a.update('007')
>>> a
set(['e', '7', 'h', 'l', '0', 'Witch', 'W'])
  • defaultdict类
     python中,访问一个不存在的字典中的键,会产生KeyError异常:
>>> dict = {'Name':'Bob', 'Age':30}
>>> dict['Name']
'Bob'
>>> dict['Title']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'Title'

 使用collections.defaultdict类
注:下面的文字来自python中defaultdict方法的使用


 defaultdict类就好像是一个dict,但是它是使用一个类型来初始化的:

>>> from collections import defaultdict
>>> dd = defaultdict(list)
>>> dd
defaultdict(<type 'list'>, {})

 defaultdict类的初始化函数接受一个类型作为参数,当所访问的键不存在的时候,可以实例化一个值作为默认值:

>>> dd['foo']
[]
>>> dd
defaultdict(<type 'list'>, {'foo': []})
>>> dd['bar'].append('quux')
>>> dd
defaultdict(<type 'list'>, {'foo': [], 'bar': ['quux']})

 需要注意的是,这种形式的默认值只有在通过dict[key]或者dict.getitem(key)访问的时候才有效。

>>> from collections import defaultdict
>>> dd = defaultdict(list)
>>> 'something' in dd
False
>>> dd.pop('something')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'pop(): dictionary is empty'
>>> dd.get('something')
>>> dd['something']
[]

 defaultdict类除了接受类型名称作为初始化函数的参数之外,还可以使用任何不带参数的可调用函数,到时该函数的返回结果作为默认值,这样使得默认值的取值更加灵活。下面用一个例子来说明,如何用自定义的不带参数的函数zero()作为defaultdict类的初始化函数的参数:

>>> from collections import defaultdict
>>> def zero():
...     return 0
...
>>> dd = defaultdict(zero)
>>> dd
defaultdict(<function zero at 0xb7ed2684>, {})
>>> dd['foo']
0
>>> dd
defaultdict(<function zero at 0xb7ed2684>, {'foo': 0})

 利用collections.defaultdict来解决最初的单词统计问题,代码如下:

from collections import defaultdict
 
strings = ('puppy', 'kitten', 'puppy', 'puppy',
           'weasel', 'puppy', 'kitten', 'puppy')
counts = defaultdict(lambda: 0)  # 使用lambda来定义简单的函数
 
for s in strings:
    counts[s] += 1

 或者:

from collections import defaultdict
 
strings = ('puppy', 'kitten', 'puppy', 'puppy',
           'weasel', 'puppy', 'kitten', 'puppy')
counts = defaultdict(int)
 
for s in strings:
    counts[s] += 1

  • Counter类
     Counter类,存在于collections包内,用于跟踪值出现的次数,是一个无序的容器类型,以字典的键值对的形式存储。
      - 创建方式:
from collections import Counter
c = Counter()	#空的Counter类
c = Counter('gallahad')	#从一个可迭代对象创建
c = Counter({'a':4, 'b':2})	#从字典创建
c = Counter(a=4, b=2)	#从键值对创建(关键字实参)

 当访问的键值不存在时,返回0,而不是keyError,否则返回它的计数。
  - 更新:update()
可使用一个可迭代对象或另一个Counter对象更新键值。

from collections import Counter
>>> c = Counter('Which')
>>> c
Counter({'h': 2, 'i': 1, 'c': 1, 'W': 1})
>>> c.update('Witch')
>>> c
Counter({'h': 3, 'i': 2, 'c': 2, 'W': 2, 't': 1})
>>> c['h']
3
>>> d = Counter('witch')
>>> d
Counter({'i': 1, 'h': 1, 'c': 1, 't': 1, 'w': 1})
>>> c.update(d)
>>> c
Counter({'h': 4, 'c': 3, 'i': 3, 't': 2, 'W': 2, 'w': 1})
>>> c['h']
4

  - 减少:subtract()

更多关于Counter的信息,可以参考阅读:collections模块—— Counter

猜你喜欢

转载自blog.csdn.net/steventian72/article/details/86550882