Week 7: 类的封装习题、二分法插入

一、随机整数生成类

#思路一:普通类实现,By me 

import random
from matplotlib import pyplot as plt

class RandomNum:
    def __init__(self,num,start=1,end=10):
        self.num=num
        self.start=start
        self.end=end

    def produce(self):
        return [random.randint(self.start,self.end) for _ in range(self.num)]

l=RandomNum(20).produce()
print(l)

def coordinate(src):
    for i in range(10):
        x,y=src[i],src[-i-1]
        plt.scatter(x,y)
    plt.show()

coordinate(l)

可以认为生成一批随机数是一个工具,只是放在一个模块下一起管理,就像math模块一样;工具类简单调用就能出结果;以下为该思路的代码,也可改为@staticmethod,行参去掉cls即可,只要配好缺省值调用起来更方便,不用传参就可保证函数调用正常工作。

类方法、静态方法更像工具,不需实例即可工作。

#思路二:用工具类实现

class RandomNum:
    @classmethod
    def produce(cls,start=1,stop=10,patch=10):
        return [random.randint(start,stop) for _ in range(self.patch)]
 
 result=RandomNum.produce()
 print(result)

生成器的取名最好以iter开头,方便使用方理解;生成器版本如下:

#思路三:生成器实现1

import random

class RandomGenerator:
    def __init__(self,start=1,stop=100,patch=10):
        self.start=start
        self.stop=stop
        self.patch=patch
        self._gen=self._generate()

    def _generate(self):  #一次产生一个数据,不给看、不给调用
    	while True:
    		yield random.randint(self.start,self.stop)

    def generate(self,count=0):  #给用户使用,并自行决定一次产生多少个数字
    	patch=self.patch if count<=0 else count
    	return [next(self._gen) for _ in range(patch)]
    	# 注意:next(self._generate())这种写法每次都调用一次生成器、取其第一个随机数,不如先让 g=self._generate(),再写成next(g);或者在__init__中, self._gen=self._generate(),因为_generate()很早就定义好,实例化之后初始化,且在self.start=start、self.stop=stop、self.patch=patch之后再做self._gen=self._generate(),调用这个生成器对象,实例直接调用

a=RandomGenerator()
print(a.generate())
print(a.generate(5))
#思路四:生成器实现2,一次yield一批数据,而不是一次yield一个、攒一批

import random

class RandomGenerator:
    def __init__(self,start=1,stop=100,patch=10):
        self.start=start
        self.stop=stop
        self.patch=patch
        self._gen=self._generate()

    def _generate(self):
    	while True:
    		yield [random.randint(self.start,self.stop) for _ in range(self.patch)]

    def generate(self,count=0):
    	if count>0:
    		self.patch=count
    	return next(self._gen)

a=RandomGenerator()
print(a.generate())
print(a.generate(5))  
#此处需注意,改动了count,即self.patch,return next(self._gen)确实拿到了5个数据,所以生成一个生成器对象时,并不是把所有参数都固定好,每次执行到yield语句会停一下、看一下当前变量、重新算一次再送出一个数据。所以其实只是固定了一个函数对象,每用一次next都会再判断一次循环、到yield时再算一次。
思路五:使用property

import random

class RandomGenerator:
    def __init__(self,start=1,stop=100,patch=10):
        self.start=start
        self.stop=stop
        self._patch=patch
        self._gen=self._generate()

    def _generate(self):
    	while True:
    		yield [random.randint(self.start,self.stop) for _ in range(self.patch)]

    def generate(self,count=0):
    	if count>0:
    		self.patch=count
    	return next(self._gen)

    @property
    def patch(self):
    	return self._patch

    @patch.setter
    def patch(self,value):
    	self._patch=value

a=RandomGenerator()
print(a.generate())
a.patch=5
print(a.generate())

二、打印坐标

注意,面向对象的思想,构建Point类,利用zip函数和参数解构

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

points=[Point(x,y) for x,y in zip(RandomGenerator().generate(),RandomGenerator().generate())]

for p in points:
	print('{}:{}'.format(p.x,p.y))

三、车辆信息

#By me,问题在于车没有增加车信息的功能,只有显示自己所有属性的功能,故应建两个类

class Cars:
    def __init__(self,mark,color,price,speed):
        self.mark=mark
        self.color=color
        self.price=price
        self.speed=speed

    def add_inf(self,attr,val):
        self.attr=val

    def all_inf(self):
        print(self.__dict__)  # 这种做法较简便,但需注意把保护成员、私有成员用filter函数过滤一下,返回一个新字典即可

honda=Cars('honda','red','30W','120')
honda.add_inf('seat','7')
honda.all_inf()

注意是两个类,一个类是纯粹车的信息;一个类用来管理:增减删改车辆信息、罗列车的属性信息等

# By Wayne

class Car:  # 记录单一车辆
    def __init__(self,mark,color,price,speed):   #因为车的属性信息很多,此处最好设计成可变参数
        self.mark=mark
        self.color=color
        self.price=price
        self.speed=speed

class CarInfo:
    def __init__(self):
        self.info=[] 

    def addcar(self,car:Car):  #命名时虽然下划线能省则省,但只要连在一起无歧义,直接写也ok
        self.info.append(car.__dict__)

    def getall(self):   #这种返回总信息的业务,不需要把这种巨量的数据都返回回来、比如超大的列表,需考虑管理控制上下限范围,人眼不可能一次看几万行,到底怎么给,用分页、一次yield一批返回最为合适,而不是一次return一个大列表
        return self.info

c=CarInfo()
car=Car('audi','red','100W','400km/h')
c.addcar(car)

print(c.getall())

以上CarInfo类的总列表可以设计为类属性、也可以设计为实例属性,但考虑到CarInfo是用来管理数据的类,类属性其实更为便利,即定义class CarInfo:后写cars=[ ],那么addcar也可写成类方法,传入cls而不是一个CarInfo实例,最后调用时CarInfo.addcar(Car())即可,因为只需要一个集中管理数据的数据中心,数据大家共用;
如果想每个实例有自己单独的数据管理容器,就需设计成上例,比如本市有3个车辆管理中心,数据直接不可共享、要分开管理,就要换一种写法进行设计。

稍作修改,如下:

# By Wayne

class Car:  # 记录单一车辆
    def __init__(self,mark,color,price,speed):   #因为车的属性信息很多,此处最好设计成可变参数
        self.mark=mark
        self.color=color
        self.price=price
        self.speed=speed

    def getcarinfo(self):
        return self.__dict__
    
    #用于呈现表达给人看的函数,方便在控制台调试;定义Car类型数据的字符串表现形式
    def __repr__(self):
        return '<{},{},{},{},{}>'.format(type(self).__name__,self.mark[:20],self.color,self.speed,self.price)

class CarInfo:
    cars=[]

    def addcar(self,car:Car):  
        self.cars.append(car)

    def get_all_car_info(self):   
        return self.cars

c=CarInfo()
car=Car('audi','red','100W','400km/h')
print(car)  # 看__repr__的呈现效果
c.addcar(car)
print(c.get_all_car_info())

#输出结果如下:
<Car,audi,red,400km/h,100W>
[<Car,audi,red,400km/h,100W>]

四、温度的转换处理

这里写图片描述

思路:温度转换可以使用实例的方法,也可以使用类方法(不创建对象,直接进行温度转换计算,这个类设计就像把温度转换工具做了一个大集合,封装成一个类),因此用classmethod和staticmethod就很合适,里面都不用构造__init__函数。

# 把convert函数作为内部函数供这6个快捷函数使用,真正转换用convert这个内部函数

class Temperature:

    def c2f(self):
        pass

    def f2c(self,f):
        return (f-32)*5/9

    def c2k(self.c):
        return c+273.15

    def k2c(self.k):
        return k-273.15

    def k2f(self):
        pass

    def f2k(self):
        pass

    def _convert(self,temperature,mode) #mode='k' or 'c'
        pass

另一种思考:先给一个温度值和单位
然后把温度都转换为中间量-摄氏度,再计算其他单位的温度值。

class Temperature:
    def __init__(self,t,unit='c'):
        self._c=None
        self._f=None
        self._k=None

    if unit =='k':
        self._k=t
        self._c=self.k2c(t)
    elif unit =='f':
        self._f=t
        self._c=self.f2c(t)
    else:
        self._c=t
   
    #定义这3个property函数时因为考虑到用户更喜好t.c,t.k,t.f这样的方式调用,为防止初始化定义的属性和这里的函数重名、出现歧义和递归调用,将原属性加上下划线,统一改变命名可在选中名字后用pycharm中的右键Refactor-Rename,
    @property
    def c(self):
        return self._c
    
    @property
    def k(self):
        if self._k is None:
            self._k=self.c2k(self._c)
        return self._k

    @property
    def f(self):
        if self._f is None:
            self._k=self.c2f(self._c)
        return self._f

    @classmethod
    def c2f(cls,c):
        return 9*c/5+32

    @classmethod
    def f2c(cls,f):
        return 5*(f-32)/9

    @classmethod
    def c2k(cls,c):
        return c+273.15

    @classmethod
    def k2c(cls,k):
        return k-273.15

    @classmethod
    def f2k(cls,f):
        return cls.c2k(cls.f2c(f))

    @classmethod
    def k2f(cls,k):
        return cls.c2f(cls.f2c(k))

print(Temperature.c2f(40))
print(Temperature.c2k(40))
print(Temperature.f2c(104))
print(Temperature.k2c(313.15))
print(Temperature.k2f(313.15))
print(Temperature.f2k(104))

t=Temperature(37)
print(t.c,t.k,t.f)

五、模拟购物车购物

#By me

class ShoppingCart:
    def __init__(self,cart={},prices=0):
        self.cart=cart
        self.prices=prices

    def buying(self,products):
        for name,price,number in products:
            self.cart[(name,price)]=self.cart.get((name,price),0)+number
            self.prices+=price*number
        return print('Cart has {}, all prices are {}.'.format(self.cart,self.prices))


class Product:
    def __init__(self,name,price,number):
        self.name=name
        self.price=price
        self.number=number

    def inf(self):
        return self.name,self.price,self.number

things=(Product('A',10,3).inf(),Product('B',20,2).inf(),Product('C',50,1).inf())
tom=ShoppingCart().buying(things)
#Teacher Wayne

class Item:   #商品类
    def __init__(self,**kwargs):   #
        self.__spec=kwargs

    def __repr__(self):
        return str(sorted(self.__spec.items()))

class Cart:
    def __init__(self):
        self.items=[]

    def additem(self,item:Item):
        self.items.append(item)

    def getallitems(self):
        return self.items

mycart=Cart()
myphone=Item(mark='Huawei',memory='4G')
mycart.additem(myphone)
mycar=Item(mark='Audi',year='2018')
mycart.additem(mycar)

print(mycart.getallitems())

#运行结果:
[[('mark', 'Huawei'), ('memory', '4G')], [('mark', 'Audi'), ('year', '2018')]]

六、二分法插入

# 算法实现:核心是折半至重合为止

src1=[37,25,99,73,48,47,40,40,25,99,51]
src2=[20,40,41,100]
orderlist=sorted(src1)

def insert_sort(orderlist,i):
    ret=orderlist
    low=0
    high=len(ret)    #注意insert插入索引应大于总长(尾部追加),否则low和high最多都只能取到length-1

    while low<high:
        mid=(low+high)//2
        if orderlist[mid]<i:
            low=mid+1
        else:
            high=mid
    ret.insert(low,i)
    return ret

for x in src2:
    print(insert_sort(orderlist,x))

应用案例:
在这里插入图片描述

import bisect

def get_grade(score):
    breakpoints=[60,70,80,90,100]
    grades='EDCBA'
    return grades[bisect.bisect(breakpoints,score)]

for student in (91,82,32,65,70,80,60):
    print('{} -> {}'.format(student,get_grade(student)))

# 运行结果:
91 -> A
82 -> B
32 -> E
65 -> D
70 -> C
80 -> B
60 -> D

猜你喜欢

转载自blog.csdn.net/weixin_42196568/article/details/82711731