CC00020.python——|Hadoop&Python.v20|——|Arithmetic.v20|语法:进阶&面向对象.V2|

一、面向对象
### --- 面向对象基本概念

~~~     # 面向过程:
~~~     根据业务逻辑从上到下写代码。
~~~     # 面向对象:
~~~     将变量与函数、属性绑定到一起,分类进行封装,每个程序只要负责分配给自己的功能,
~~~     这样能够更快速的开发程序,减少了重复代码。
~~~     我们在前面写的代码都是面向过程的,这对初学者比较容易接受,
~~~     而且,对于比较简单的需求,用面向过程实现起来确实更简单。
### --- 那什么是对象呢 ?

~~~     我们可以理解为实际存在的实物,比如一个用户、一台ATM机、一幢建筑,
~~~     亦或者是软件业务流程中产生的虚拟概念,比如订单、购物车、用户账户。
~~~     我们发现,不管是实体还是虚拟产物,它们都是有一些共同点,都是名词,有自己的行为,
~~~     有自己的属性。比如说用户对象,用户都有相同的几个属性,名字、年龄、性别、注册时间、
~~~     上一次登录的时间等等。但不同用户这个几属性的值却都不一样。下面我们来看几个例子。
~~~     如果你想向另一个人描述一只狗,
### --- 那你会说出这只狗的哪几个特点?
~~~     品种
~~~     颜色
~~~     体型大小

~~~     # 狗会有哪些行为呢?
~~~     吃东西
~~~     奔跑
~~~     吠叫
~~~     这些都是我们基于常识总结出来的狗的特点和行为,
~~~     对象的属性就是可以精确描述事物的特点,对象的函数就是事物的行为。
二、类和实例
~~~     # 现在我们用代码来实现一下“狗”对象先介绍Python里的一个重要概念:类它也是面试对象编程的基础。
class Dog:
pass

~~~     # 这样我们就定义了一个类,使用class关键字,加上一个类名,这样我们就定义了一个空的类。
~~~ 类名一般使用名词,且使用驼峰式命名法。
~~~     # 类是创建对象的模板,对象是类的实例。类就像生产线,有了类,就可以生产出许许多多的相同对象。
~~~     # 使用上面的Dog类来创建一个Dog对象:
dog = Dog()
type(dog)

~~~     # 这里dog就是Dog的实例,通过内置的type函数,可以查看任何对象的类。
type(1)
type('abc')
type([])
~~~     # 这些都是我们学习过的数据类型,我们看到它们分别是整数、字符串和列表。那dog的类型就是Dog。
~~~     # 如果我们不知道一个对象是不是某种类型,就可以用type判断。
type('abc') == str # True
type(dog) == Dog # True
type(1) == int # True

~~~     # 也可以使用内置函数isinstance来判断对象与类的关系
isinstance('abd', str) # True
isinstance(1, int) # True
isinstance(dog, Dog) # True
三、对象的属性与方法
### --- 现在我们来完整的实现一下Dog类:

class Dog:
def __init__(self):
self.breed = None
self.color = None
self.size = None
def eat(self):
print("I like bones")
def run(self):
print("I'll catch you.")
def bark(self):
print('Wang! Wang!')
~~~     # 大家应该注意到,类的每一个方法的第一个参数是self ,但在调用的时候却不需要传参数给它。它是类方法和普通函数的区别,这个self代表的是实例自身,意思是“我的”,现在我们来创建Dog的实例

dog = Dog()
dog.eat()
dog.run()
dog.bark()
print('一只%s型%s色的%s' % (dog.size, dog.color, dog.breed))
~~~     # 调用不同的方法能打印出不同的内容,体现了不同的行为。但是最后一句话打印出来的内容却是None,因为我们还没有设置相应的属性。

dog.breed = '哈士奇'
dog.color = '黑白'
dog.size = '大'
print('一只%s型%s色的%s' % (dog.size, dog.color, dog.breed))

~~~     # 一只大型黑白色的哈士奇
~~~     # 如果每个创建完每个对象之后还要一个个的设置属性,会很麻烦。我们可以使用__init__ 函数来接收初始化参数,这样就可以把属性的值作为参数在初始化对象的时候就传给它。大家应该也注意到了,__init__ 函数看起来与众不同的样子,它是Python的类用来初始化对象的构造函数,它的名字是固定的,必须这样写,创建对象时会首先调用它。改完后构造函数后代码如下:

class Dog:
def __init__(self, size, color, breed='土狗'):
self.breed = breed
self.color = color
self.size = size
def eat(self):
print("I like bones")
def run(self):
print("I'll catch you.")
def bark(self):
print('Wang! Wang!')
~~~     # 现在再来创建对象:

dog = Dog('中', '黄')
print('一只%s型%s色的%s' % (dog.size, dog.color, dog.breed))
~~~     # 第三个参数breed因为给了它默认值,所以可以不用传。
~~~     # 我们在之前学习的字符串、列表的所有函数,实际是调用字符串对象、列表对象的方法。
~~~     # 对象自身的属性是直接可以方法里使用的,比如我们改造一下bark方法,让狗可以开口自我介绍

class Dog:
def __init__(self, size, color, breed='土狗'):
self.breed = breed
self.color = color
self.size = size
def eat(self):
print("I like bones")
def run(self):
print("I'll catch you.")
def bark(self):
print('我是一只%s型%s色的%s' % (self.size, self.color, self.breed))
~~~     # 这里在bark方法里使用的self和构造函数里的self一样,都是指向对象自身。

dog = Dog('中', '黄')
print('一只%s型%s色的%s' % (dog.size, dog.color, dog.breed))
四、类属性与方法
~~~     # 对象是从类创造的,对象的属性和方法虽然是在类中定义的,但它们的值是各个对象独有的,互相不能共享。而类也有属性和方法,且它们可以和类创建的所有对象共享。我们先来定义一个类

class Goods:
def __init__(self):
self.name = ''
self.price = 0
self.discount = 1
~~~     # Goods类有三个对象属性,每个商品有自己的名称、价格、折扣。我可以随意的创建商品

g1 = Goods()
g2 = Goods()
~~~     # 但如何知道一共创建了多少个商品呢?我们可以给Goods类加一个计数器。

class Goods:
count = 0
def __init__(self):
Goods.count += 1
self.name = ''
self.price = 0
self.discount = 1
~~~     # 我们给Goods类加了一个属性count,每当调用__init__ 函数时将它加1,这样我们就可以知道一共创建了多少商品对象了。这个count就是类属性,它可以通过对象访问,也可以通过类访问。

g1 = Goods()
g1.count # 1
g2 = Goods()
Goods.count # 2
~~~     # 即使没有定义对象,也可以直接访问count属性,这就是类属性,同样,类方法也不需要创建对象,通过类名就可以访问。我们改造一下Goods类,给它增加一个属性id,表示商品唯一的序列号,为了保证id不重复,我们使用计数器,每创建一个商品给它加1。

class Goods:
id_count = 0
@classmethod
def generate_id(cls):
cls.id_count += 1
return cls.id_count
def __init__(self):
~~~     # zfill函数表示用“0”将数字补足5位

self.id = str(self.generate_id()).zfill(5)
self.name = ''
self.price = 0
self.discount = 1
~~~     # 这里的generate_id 方法就是一个类方法,它的上面一行有一个@classmethod ,声明了它是类方法,它的参数不再是self,而是cls,指向类本身,用来在类方法内部访问类成员属性和方法。这个方法每次将id_count属性加1,并返回。

~~~ 这种@ 符号的写法叫做装饰器,装饰器是用来装饰函数的,不同的装饰器赋予函数不同的特殊功
~~~ 能。对于classmethod装饰器,大家只要知道它是用来定义类方法的就行了。
~~~     # 现在我们再来试试:

g1 = Goods()
g2 = Goods()
g1.id # 00001
g2.id # 00002
Goods.id_count # 2
五、一切皆对象
~~~     # 在Python中一切都是对象,我们使用的数字、字符串、函数甚至类本身,都是对象。所有的对象都可以用type函数来判断它的类型,同时可以用dir函数来查看它的属性和方法。

dir(dog)
~~~     # 会显示出dog对象的所有属性和方法,包括刚刚定义的那几个方法和属性。对于对象,也可以使用help函数查看它的帮助文档。

help(sum)
help(print)
~~~     # 这些帮助信息可以在定义的时候写入到代码里:

def bark(self):
"""一只狗的自我介绍"""
print('我是一只%s型%s色的%s' % (self.size, self.color, self.breed))
~~~     # 加上这句文档后,我们就可以使用help函数查看bark方法的帮助信息了,这有助于其他人使用我们的方法。

help(dog.bark)
help(Dog.bark)
~~~     # 一切皆对象是一句简单的话,但它的精神却很深邃,试试下面的代码,你还能看得懂吗?

lst = []
lst.append(Dog)
dog = lst[0]('中', '黄')
lst.append(dog)
lst[1].bark()
lst[1].sleep = lambda: print('Good night.')
lst.pop().sleep()
~~~     # 有时候两个对象的值完全相同,我们可以说这两个对象是相等的,但不能说它们是同一个对象。

l1 = [1, 2, 3]
l2 = [1, 2,]
l1 == l2 # False
l2.append(3)
l1 == l2 # True
l1 is l2 # False
~~~     # 最后一行操作,使用is 关键字来判断这两个对象是否是同一个对象,结果是False。它表明l1和l2是不同的对象,这一点可以使用id函数看出来:
~~~     # 分别返回两串不同的数字,这个一长串的数字代表了对象所指向的内存空间地址。

id(l1)
id(l2)
六、综合案例-电商购物车商品统计分析
### --- 项目需求

~~~     可以设置每个商品的名称、价格、折扣率,用户将商品加入到购物车以后,
~~~     能够立即显示所有商品、总价、折扣情况和实际需要支付的金额,也就是折扣后的金额。
~~~     商品的名称、价格、折扣率都可以随意修改,且修改完成后,
~~~     购物车中的相关信息和金额也会发生改变。
~~~     需求分析:在这个需求里面,提到了两个虚拟产物,商品与购物车,也就是说需要定义两个类。
### --- 编程实现

class Goods:
    """商品类"""
    id_count = 0\
    @classmethod
    def generate_id(cls):
        cls.id_count += 1
        return cls.id_count
    def __init__(self, name, price, discount=1):
        self.id = str(self.generate_id()).zfill(5)
        self.name = name
        self.price = price
        self.discount = discount
        def calc_price(self):
            """计算商品打折后的实际价格"""
            return self.price * self.discount
### --- 这是商品类,它有四个属性:ID、商品名称、价格、折扣,另外它还有一个函数,计算出商品打完折后的价格。接下来我们来创建几个商品对象:

g1 = Goods('iPhone 11', 6000, 0.9)
g2 = Goods('U盘32G', 100, 0.8)
g3 = Goods('华为P40', 5000)
### --- 这样我们就创建了三个商品对象,并设置好了它们的名称、价格、折扣。接下来我们来编写购物车类:

class Cart:
    """购物车"""
    def __init__(self):
        self.cart = {}
        self.goods_list = []
    def add(self, goods, num=1):
    """向购物车中添加商品"""
    if goods in self.goods_list:
        self.cart[goods.id] = self.cart[goods.id] + num
    else:
        self.goods_list.append(goods)
        self.cart[goods.id] = num
    def remove(self, goods, num):
    """从购物车减少或删除商品"""
    if goods not in self.goods_list:
        return self.cart[goods.id] -= num
    if self.cart[goods.id] <= 0:
        del self.cart[goods.id] self.goods_list.remove(goods)
        def get_goods_by_id(self, id):
    """根据商品名称查找商品"""
    for g in self.goods_list:
        if g.id == id:
        return g
    def get_total_amount(self):
    """获取当前购物车中的总价"""
    amount = 0
    for name, num in self.cart.items():
        goods = self.get_goods_by_id(name)
        amount += goods.price * num
        return amount
    def get_pay_amount(self):
    """获取实际需要支付的总价"""
    amount = 0
    for name, num in self.cart.items():
        goods = self.get_goods_by_id(name)
        amount += goods.calc_price() * num
        return amount
    def show(self):
    """显示当前购物车中所有商品的数量、价格,以及总价"""
    title = ('商品', '单价', '数量', '价格(元)')
    def show_row(row):
    """内部函数,显示购物车中的一行"""
    for col in row:
        print(str(col).ljust(12), end='\t')
        print()
        print("-" * 70)
        show_row(title)
    for id, num in self.cart.items():
        goods = self.get_goods_by_id(id)
        price = '%.2f' % goods.price
    if goods.discount < 1:
        price = '%.2f (%d折)' % (goods.price, goods.discount * 10)
        show_row((goods.name, price, num, '%.2f' % (goods.calc_price() * num))) total_amount = self.get_total_amount()pay_amount = self.get_pay_amount() discount_amount = total_amount - pay_amountshow_row(('', '', '', '总金额: %.2f' % total_amount))
        if discount_amount > 0:
            show_row(('', '', '', '优惠: %.2f' % discount_amount))
            show_row(('', '', '', '实付金额: %.2f' % pay_amount))
### --- 这是购物车类,看起来它的代码很长。共有两个属性,6个方法。其实这个类主要就是提供了三个功能:
~~~     增加商品 add
~~~     减少商品 remove
~~~     显示商品 show
~~~     其他的函数都是辅助实现这三个主要功能。cart是一个字典,用来保存商品和数量的对应关系,它的键名是商品ID(字符串);goods_list是一个列表,保存了购物车所有商品的详细信息(商品类实例),注意它们的数据结构。
~~~     有了Goods和Cart,我们就可以随意的增加删除商品,并可以随时查看购物车里的情况。

cart = Cart()
cart.add(g1)
cart.add(g2, 3)
cart.show()
### --- 显示如下:

商品 单价 数量 价格(元)
iPhone 11 6000.00 (9折) 1 5400.00
U盘32G 100.00 (8折) 3 240.00
总金额: 6300.00
优惠: 660.00
实付金额: 5640.00
### --- 我们可以继续增加或者删除,并随时可以查看购物车的商品、计算总金额。如果商品的数量为零,则会从购物车中被删除

cart.remove(g2, 2)
cart.show()
cart.remove(g2, 1)
cart.show()
### --- 如果商品的名称、价格或者折扣发生了变化,我们只需要修改商品对象就可以了,其他的代码都不用修改,购物车中的信息会实时的跟随调整。
~~~     # 可以看到,在修改了g3对象的商品名称之后,再次显示购物车时发生了变化 ,而我们的Cart类不用修改任何代码,这样做到了不同实体之间的操作隔离,是不是有点感受到面向面向对象的便捷了呢?

cart.add(g3)
cart.show()
g3.name = '华为P40 pro'
cart.show()
七、自定义属性-property
### --- Cart类中的这两个函数get_total_amount 和get_pay_amount ,每次调用它们的时候都是直接调用,没有传递任何参数,最后返回一个值。对于这种方法,其实可以把它们变成property(属性):

@property
def total_amount(self):
    """获取当前购物车中的总价"""
    amount = 0
    for name, num in self.cart.items():
        goods = self.get_goods_by_id(name)
        amount += goods.price * num
    return amount
@property
def pay_amount(self):
    """获取实际需要支付的总价"""
    amount = 0
    for name, num in self.cart.items():
        goods = self.get_goods_by_id(name)
        amount += goods.calc_price() * num
        return amount
### --- 我们在函数名前面加了一个property装饰器,修改方法名,去掉了get_ ,其实方法名也可以不改,在这里修改是为了代码的可读性更通顺。改造后,使用这两个方法时,就可以像使用普通属性一样:
~~~     这样使用起来是不是更简洁优雅了呢?
~~~     继承*
~~~     魔术方法*

cart.total_amount
cart.pay_amount
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yanqi_vip

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值