【20天快速掌握Python】day10-面向对象基础

20 篇文章 0 订阅
19 篇文章 1 订阅

1.面向对象介绍

面向对象与面向过程

  • 面向过程:根据业务逻辑从上到下写代码。

  • 面向对象:将变量与函数绑定到一起,分类进行封装,每个程序只要负责分配给自己的分类,这样能够更快速的开发程序,减少了重复代码。

面向过程编程最易被初学者接受,其往往用一长段代码来实现指定功能,开发过程的思路是将数据与函数按照执行的逻辑顺序组织在一起,数据与函数分开考虑,面向过程基本是由函数组成的。

1.1 面向过程编程

面向过程编程的关注点在于怎么做

  • 把完成某一个需求的 所有步骤 从头到尾 逐步实现

  • 根据开发需求,将某些 功能独立 的代码 封装 成一个又一个 函数

  • 最后完成的代码,就是顺序地调用 不同的函数

特点:

  • 注重步骤与过程,不注重职责分工

  • 如果需求复杂,代码会变得很复杂

  • 开发复杂项目,没有固定的套路,开发难度很大!

1.2 面向对象基本概念

面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)和面相过程编程,是两种不同的编程方式。

面向对象编程的关注点在于谁来做

相比较函数,面向对象是更大的封装,根据职责在 一个对象中封装多个方法

  • 在完成某一个需求前,首先确定职责 —— 要做的事情(方法)

  • 根据 职责 确定不同的 对象,在对象内部封装不同的方法(多个)

  • 最后完成的代码,就是顺序地调用不同对象的相应方法。

特点:

  • 注重 对象和职责,不同的对象承担不同的职责。

  • 更加适合应对复杂的需求变化,是专门应对复杂项目开发,提供的固定套路。

  • 需要在面向过程基础上,再学习一些面向对象的语法。

2.类和对象

对象是面向对象编程的两个核心概念。

2.1 类

类是对一群具有相同特征或者行为 的事物的一个统称,是抽象的,不能直接使用

  • 特征其实就是一个变量,在类里我们称之为属性。

  • 行为其实就是一个函数,在类里我们称之为方法。

  • 类其实就是由 属性方法 组成的一个抽象概念。

类就相当于制造飞机时的图纸,是一个模板。这个模板只规定了飞机的某些特征(例如大小,颜色,型号等等)和行为(例如起飞,降落,飞行等等),它并不是一个具体的飞机,而是对飞机的一个抽象概念。它出现的目的,是为了让我们的创建飞机对象。

2.2 对象

对象是由类创建出来的一个具体存在,可以直接使用。由哪一个类创建出来的 对象,就拥有在哪一个类中定义的属性和方法。 对象 就相当于用图纸制造的飞机。在开发中,应该先有类,在类里定义好属性和行为,再根据类来创建对象。

类和对象的关系

  • 类是模板,对象是根据类这个模板创建出来的,应该先有类,再有对象。

  • 使用同一个类,能够创建出很多对象。

  • 类中定义了什么属性和方法,对象中就有什么属性和方法。

  • 不同对象对应的属性值也会不同。

例如:定义了一个狗类,这个狗类有以下属性:

  • 品种

  • 颜色

  • 性别

  • 名字

现在根据这个类创建出了两条狗,这两条狗分别是 哈士奇、灰色、母、二哈中华田园犬、黄色、公、旺财。我们发现,这两条狗都具有 品种、颜色、性别和名字这些属性,但是每条狗对应的属性值却不一样。

2.3 类的设计

在使用面相对象开发前,应该首先分析需求,确定一下,程序中需要包含哪些类!

在程序开发中,要设计一个类,通常需要满足一下三个要素:

  1. 类名 这类事物的名字,安照大驼峰命名法(每个单词的首字母大写)起名。

  2. 属性 这类事物具有什么样的特征。

  3. 方法 这类事物具有什么样的行为。

2.4 定义类名

名词提炼法:分析整个业务流程,出现的名词,通常就是找到的类。

2.5 属性和方法的确定

  • 对对象的特征描述,可以定义成属性

  • 对象具有的行为(动词)可以定义成方法

3.面向对象基本用法

3.1 定义简单的类(只包含方法)

面向对象是更大的封装,在一个类中封装多个方法,这样通过这个类创建出来的对象,就可以直接调用这些方法了!

定义类

在Python中要定义一个只包含方法的类,语法格式如下:

class 类名:
    def 方法1(self,参数列表):
        pass
    def 方法2(self,参数列表):
        pass
  1. 方法的定义格式和之前学习过的函数一样

  2. 方法里的第一个参数必须是self,大家暂时先记住,稍后介绍 self.

  3. 类名要遵守大驼峰命名法。

创建实例对象

当一个类定义完成之后,要使用这个类来创建对象,语法格式如下:

对象变量名 = 类名()

第一个面向对象代码

需求

  • 小猫 爱 吃 鱼,小猫 要 喝 水

分析

  • 定义一个猫类 Cat

  • 定义两个方法 eat 和 drink

  • 按照需求 —— 不需要定义属性

class Cat:
    """这是个猫类"""

    def eat(self):
        print("小猫在吃东西")

    def drink(self):
        print("小猫在喝水")

tom = Cat()  # 创建了一个Cat对象
tom.eat()
tom.drink()

hello_kitty = Cat()  # 又创建了一个新的Cat对象
hello_kitty.eat()
hello_kitty.drink()

思考:tomhello_kitty 是同一个对象吗?

3.2 self的使用

给对象添加属性

python支持动态属性,当一个对象创建好了以后,直接使用 对象.属性名 = 属性值 就可以很方便的给对象添加一个属性。

tom = Cat()
tom.name = 'Tom'  # 可以直接给 tom 对象添加一个 name 属性

这种方法很方便,但是,不建议使用这种方式给对象添加属性。

self的概念

哪个对象调用了方法,方法里的self指的就是谁。 通过 self.属性名 可以访问到这个对象的属性;通过 self.方法名() 可以调用这个对象的方法。

class Cat:
    def eat(self):
        print("%s爱吃鱼" %self.name)

tom = Cat()
tom.name = 'Tom'  # 给 tom 对象添加了一个name属性
tom.eat()  # Tom爱吃鱼

lazy_cat = Cat()
lazy_cat.name = "大懒猫"
lazy_cat.eat() # 大懒猫爱吃鱼

直接给对象添加属性的缺点

上述代码中,我们是先创建对象,然后再给对象添加 name 属性,但是这样做会有问题。

tom = Cat()
tom.eat()
tom.anme = "Tom"

程序运行时会报错:

AttributeError: 'Cat' object has no attribute 'name'
错误提示:'Cat'对象没有 'name' 属性

在日常开发中,不推荐在类的外部直接给对象添加属性这种方式。对象应该具有哪些属性,我们应该封装在类的内部。

4.魔法方法

Python 里有一种方法,叫做魔法方法。Python 的类里提供的,两个下划线开始,两个下划线结束的方法,就是魔法方法,魔法方法在恰当的时候就会被激活,自动执行。 魔法方法的两个特点:

  • 两侧各有两个下划线;

  • "咒语"名字已经由 Python 官方定义好,我们不能乱写。

4.1 __init__方法

__init__()方法,在创建一个对象时默认被调用,不需要手动调用。在开发中,如果希望在创建对象的同时,就设置对象的属性,可以对 __init__ 方法进行改造。

class Cat:
    """这是一个猫类"""
    def __init__(self,name):  # 重写了 __init__ 魔法方法
        self.name = name

    def eat(self):
        return "%s爱吃鱼"%self.name
    def drink(self):
        return '%s爱喝水'%self.name

    """
        tom = Cat()
        TypeError: __init__() missing 1 required positional argument: 'name'
        这种写法在运行时会直接报错!因为 __init__ 方法里要求在创建对象时,必须要传递 name 属性,如果不传入会直接报错!
    """

tom = Cat("Tom")  # 创建对象时,必须要指定name属性的值
tom.eat()   # tom爱吃鱼

注意:

  1. __init__()方法在创建对象时,会默认被调用,不需要手动的调用这个方法。

  2. __init__()方法里的self参数,在创建对象时不需要传递参数,python解释器会把创建好的对象引用直接赋值给self

  3. 在类的内部,可以使用self来使用属性和调用方法;在类的外部,需要使用对象名来使用属性和调用方法。

  4. 如果有多个对象,每个对象的属性是各自保存的,都有各自独立的地址。

  5. 方法是所有对象共享的,只占用一份内存空间,方法被调用时会通过self来判断是哪个对象调用了实例方法。

4.2 __del__ 方法

创建对象后,python解释器默认调用__init__()方法;

而当删除对象时,python解释器也会默认调用一个方法,这个方法为__del__()方法。

class Student:
    def __init__(self,name,score):
        print('__init__方法被调用了')
        self.name = name
        self.score = score

    def __del__(self):
        print('__del__方法被调用了')
s = Student('lisi',95)
del s
input('请输入内容')

4.3 __str__方法

__str__方法返回对象的描述信息,使用print()函数打印对象时,其实调用的就是这个对象的__str__方法。

class Cat:
    def __init__(self,name,color):
        self.name = name
        self.color = color

tom = Cat('Tom','white')

# 使用 print 方法打印对象时,会调用对象的 __str__ 方法,默认会打印类名和对象的地址名
print(tom)   # <__main__.Cat object at 0x0000021BE3B9C940>

如果想要修改对象的输出的结果,可以重写 __str__ 方法。

class Person:
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def __str__(self):
        return '哈哈'

p  = Person('张三',18)
print(p)   # 哈哈  打印对象时,会自动调用对象的 __str__ 方法

一般情况下,我们在打印一个对象时,可能需要列出这个对象的所有属性。

class Student:
    def __init__(self,name,score):
        self.name = name
        self.score = score
    def __str__(self):
        return '姓名是:{},成绩是{}分'.format(self.name,self.score)

s = Student('lisi',95)
print(s)   # 姓名是:lisi,成绩是95分

4.4 __repr__方法

__repr__方法和__str__方法功能类似,都是用来修改一个对象的默认打印内容。在打印一个对象时,如果没有重写__str__方法,它会自动来查找__repr__方法。如果这两个方法都没有,会直接打印这个对象的内存地址。

class Student:
    def __init__(self, name, score):
        self.name = name
        self.score = score

    def __repr__(self):
        return 'helllo'


class Person:
    def __repr__(self):
        return 'hi'

    def __str__(self):
        return 'good'


s = Student('lisi', 95)
print(s)  # hello

p = Person()
print(p)  # good

4.5 __call__方法

对象后面加括号,触发执行。

class Foo:
    def __init__(self):
        pass

    def __call__(self, *args, **kwargs):
        print('__call__')


obj = Foo()  # 执行 __init__
obj()  # 执行 __call__

总结:

  1. 当创建一个对象时,会自动调用__init__方法,当删除一个对象时,会自动调用__del__方法。

  2. 使用__str____repr__方法,都会修改一个对象转换成为字符串的结果。一般来说,__str__方法的结果更加在意可读性,而__repr__方法的结果更加在意正确性(例如:datetime模块里的datetime类)

4.6 运算相关的魔法方法

思考:

class Person:
  def __init__(self,name,age):
    self.name = name
    self.age = age

p1 = Person('zhangsan',18)
p2 = Person('zhangsan',18)
print(p1 == p2)

上述代码中,使用==运算符比较两个对象,结果是True还是False?==到底比较的是什么?

4.7 比较运算符相关魔法方法

class Student:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __eq__(self, other):
        return self.name == other.name and self.age == other.age

    # def __ne__(self, other):

    def __lt__(self, other):
        return self.age < other.age

    # def __gt__(self, other):

    def __le__(self, other):
        return self.age <= other.age
    # def __ge__(self, other):


s1 = Student('zhangsan', 18)
s2 = Student('zhangsan', 18)
s3 = Student('lisi', 20)
print(s1 == s2)
print(s1 != s2)
print(s1 > s2)
print(s1 >= s2)
print(s1 <= s2)
print(s1 <= s2)

4.8 算数运算符相关魔法方法

class Student:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __add__(self, other):
        return self.age + other

    def __sub__(self, other):
        return self.age - other

    def __mul__(self, other):
        return self.age * other

    def __truediv__(self, other):
        return self.age / other

    def __mod__(self, other):
        return self.age % other

    def __pow__(self, power, modulo=None):
        return self.age ** power


s = Student('zhangsan', 18)
print(s + 1)  # 19
print(s - 2)  # 16
print(s * 2)  # 36
print(s / 5)  # 3.6
print(s % 5)  # 3
print(s ** 2)  # 324

4.9 类型转换相关魔法方法

class Student:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __int__(self):
        return self.age

    def __float__(self):
        return self.age * 1.0

    def __str__(self):
        return self.name

    def __bool__(self):
        return self.age > 18


s = Student('zhangsan', 18)
print(int(s))
print(float(s))
print(str(s))
print(bool(s))

if s:
    print('hello')

5.内置属性

使用内置函数dir可以查看一个对象支持的所有属性和方法,Python中存在着很多的内置属性。

5.1 __solts__

Python中支持动态属性,可以直接通过点语法直接给一个对象添加属性,代码更加的灵活。但是在某些情况下,我们可能需要对属性进行控制,此时,就剋使用slots实现。

class Person(object):
    __slots__ = ('name', 'age')
    def __init__(self, name, age):
        self.name = name
        self.age = age
p = Person('张三', 18)
p.name = '李四'

# 对象p只能设置name和age属性,不能再动态添加属性
# p.height = 180 # 报错

5.2 __doc__

表示类的描述信息。

class Foo:
    """ 描述类信息,这是用于看片的神奇 """
    def func(self):
        pass

print(Foo.__doc__)
#输出:类的描述信息

5.3 __module__和__class__

module 表示当前操作的对象在那个模块;class 表示当前操作的对象的类是什么。

test.py
class Person(object):
    def __init__(self):
        self.name = 'laowang'
main.py
from test import Person

obj = Person()
print(obj.__module__)  # 输出 test 即:输出模块
print(obj.__class__)  # 输出 test.Person 即:输出类

5.4 __dict__

以字典的形式,显示对象所有的属性和方法。

class Province(object):
    country = 'China'

    def __init__(self, name, count):
        self.name = name
        self.count = count

    def func(self, *args, **kwargs):
        print('func')

# 获取类的属性,即:类属性、方法、
print(Province.__dict__)
# 输出:{'__dict__': <attribute '__dict__' of 'Province' objects>, '__module__': '__main__', 'country': 'China', '__doc__': None, '__weakref__': <attribute '__weakref__' of 'Province' objects>, 'func': <function Province.func at 0x101897950>, '__init__': <function Province.__init__ at 0x1018978c8>}

obj1 = Province('山东', 10000)
print(obj1.__dict__)
# 获取 对象obj1 的属性
# 输出:{'count': 10000, 'name': '山东'}

obj2 = Province('山西', 20000)
print(obj2.__dict__)
# 获取 对象obj1 的属性
# 输出:{'count': 20000, 'name': '山西'}

6.类属性和对象属性

在面向对象开发中,使用类创建出来的实例是一个对象,那么,类是否是一个对象呢?

6.1 实例属性

通过类创建的对象被称为 实例对象,对象属性又称为实例属性,记录对象各自的数据,不同对象的同名实例属性,记录的数据各自独立,互不干扰。

class Person(object):
    def __init__(self,name,age):
        # 这里的name和age都属于是实例属性,每个实例在创建时,都有自己的属性
        self.name = name
        self.age = age

# 每创建一个对象,这个对象就有自己的name和age属性
p1 = Person('张三',18)
p2 = Person("李四",20)

6.2 类属性

类属性就是类对象所拥有的属性,它被该类的所有实例对象所共有,类属性可以通过类对象或者实例对象访问。

class Dog:
    type = "狗"  # 类属性

dog1 = Dog()
dog2 = Dog()

# 不管是dog1、dog2还是Dog类,都可以访问到type属性
print(Dog.type)  # 结果:狗
print(dog1.type)  # 结果:狗
print(dog2.type)  # 结果:狗

注意:

  1. 尽量避免类属性和实例属性同名。如果有同名实例属性,实例对象会优先访问实例属性

    class Dog(object):
        type = "狗"  # 类属性
    
        def __init__(self):
            self.type = "dog"  # 对象属性
    
    # 创建对象
    dog1 = Dog()
    
    print(dog1.type)     # 结果为 “dog”   类属性和实例属性同名,使用 实例对象 访问的是 实例属性
  2. 类属性只能通过类对象修改,不能通过实例对象修改

    lass Dog(object):
        type = "狗"  # 类属性
    
    # 创建对象
    dog1 = Dog()
    dog1.type = "dog"   # 使用 实例对象 创建了对象属性type
    
    print(dog1.type)     # 结果为 “dog”   类属性和实例属性同名,访问的是实例属性
    print(Dog.type)      # 结果为 "狗"   访问类属性
    
    # 只有使用类名才能修改类属性
    Dog.type = "土狗"
    print(Dog.type)  # 土狗
    dog2 = Dog()
    print(dog2.type)  # 土狗
  3. 类属性也可以设置为私有,前边添加两个下划线。 如:

    class Dog(object):
        count = 0  # 公有的类属性
        __type = "狗"  # 私有的类属性
    
    print(Dog.count)       # 正确
    print(Dog.__type)      # 错误,私有属性,外部无法访问。

7.私有属性和方法

在实际开发中,对象的某些属性或者方法可能只希望在对象的内部别使用,而不希望在外部被访问到,这时就可以定义私有属性和私有方法。

7.1 定义方法

在定义属性或方法时,在属性名或者方法名前增加两个下划线__,定义的就是私有属性或方法。

class Person:
    def __init__(self,name,age):
        self.name = name
        self.age = age
        self.__money = 2000  # 使用 __ 修饰的属性,是私有属性

    def __shopping(self, cost):
        self.__money -= cost  # __money 只能在对象内部使用
        print('还剩下%d元'%self.__money)

    def test(self):
        self.__shopping(200)  # __shopping 方法也只能在对象内部使用

p = Person('张三',18)
# print(p.__money)   这里会报错,不能直接访问对象内部的私有属性
p.test()
# p.__shopping()  这里会报错,__shopping 只能在对象内部使用,外部无法访问

7.2 访问私有属性和方法

私有属性不能直接使用,私有方法不能直接调用。但是,通过一些代码,我们也可以在外部访问一个对象的私有属性和方法。

直接访问:

使用方式:在私有属性名或方法名前添加 _类名

class Person:
    def __init__(self,name,age):
        self.name = name
        self.age = age
        self.__money = 2000

    def __shopping(self, cost):
        self.__money -= cost


p = Person('李四',20)
print(p._Person__money)  # 使用对象名._类名__私有属性名 可以直接访问对象的私有属性
p._Person__shopping(100)  # 使用对象名._类名__函数名 可以直接调用对象的私有方法
print(p._Person__money)
Copy

注意:在开发中,我们强烈不建议使用 对象名._类名__私有属性名 的方式来访问对象的私有属性!

定义方法访问私有变量:

在实际开发中,如果对象的变量使用了__ 来修饰,就说明它是一个私有变量,不建议外部直接使用和修改。如果硬要修改这个属性,可以使用定义getset方法这种方式来实现。

class Person:
    def __init__(self,name,age):
        self.name = name
        self.age = age
        self.__money = 2000  # __money 是私有变量,外部无法访问

    def get_money(self):  # 定义了get_money 方法,在这个方法里获取到 __money
        return self.__money  # 内部可以访问 __money 变量

    def set_money(self,money): # 定义了set_money 方法,在这个方法里,可以修改 __money
        self.__money = money

p = Person('王五',21)

# 外部通过调用 get_money 和 set_money 这两个公开方法获取和修改私有变量
print(p.get_money())
p.set_money(8000)
print(p.get_money())

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值