Python中面向对象(学习笔记)

Posted Aspirantlu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python中面向对象(学习笔记)相关的知识,希望对你有一定的参考价值。

文章目录

一、面向过程与面向对象

简述

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

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

面向过程编程

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

  • 把完成某一个需求的 所有步骤 从头到尾 逐步实现
  • 根据开发需求,将某些 功能独立 的代码 封装 成一个又一个 函数
  • 最后完成的代码,就是顺序地调用 不同的函数

特点:

  • 注重步骤与过程,不注重职责分工
  • 如果需求复杂,代码会变得很复杂
  • 开发复杂项目,没有固定的套路,开发难度很大

面向对象编程

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

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

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

  • 在完成某一个需求前,首先确定职责,也就是要做的事情(方法)
  • 根据 职责 确定不同的 对象,在对象内部封装不同的方法(多个)
  • 最后完成的代码,就是顺序地调用不同对象的相应方法。

特点:

  • 注重 对象 和 职责,不同的对象承担不同的职责。
  • 更加适合应对复杂的需求变化,是专门应对复杂项目开发,提供的固定套路。

二、类和对象

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

  • 特征其实就是一个变量,在类里我们称之为属性
  • 行为其实就是一个函数,在类里我们称之为方法
  • 类其实就是由 属性方法 组成的一个抽象概念。

对象(实例)

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

类和对象的关系

  • 类是模板,对象是根据类这个模板创建出来的,应该先有类,再有对象。
  • 使用同一个类,能够创建出很多对象。
  • 类中定义了什么属性和方法,对象中就有什么属性和方法。
  • 不同对象对应的属性值也会不同。

三、类的定义

class 类名():
	def 方法1(self,参数列表):
    	pass
    def 方法2(self,参数列表):
        pass

在实际项目开发中,建议类的定义和模块结合使用(一个模块中定义一个类),相同或者相似的类可以使用包进行管理。

四、类中的成员

类属性

方式一:通过 类名.类属性 访问

方式二:通过 实例对象.类属性 访问

class Person(object):
    # 类属性
    num = 10


p = Person()

# 方式一
print(Person.num)

# 方式二
print(p.num)

实例函数(对象函数,成员函数)

class Person():
    # 实例函数,对象函数,成员函数
    def eat(self, food):
        print("eating" + food)

实例函数和普通函数的区别
实例函数的形参列表第一个参数是self,而普通则不是。
关于self
self表示当前对象
1. self并不是一个关键字,其实可以是任意的标识符,为了表达代表的是当前对象自己,习惯用self
2. 调用实例函数的时候,self不需要手动传参,系统会自动传递当前的对象
3. 哪个对象调用了方法,方法里的self指的就是谁。
通过 self.属性名 可以访问到这个对象的属性。
通过 self.方法名() 可以调用这个对象的方法。
创建对象(实例化对象)

语法:变量名 = 类名(初始值) ,目前初始值省略。

class Person(object):
    def eat(self, food):  # 实例方法有一个参数self,指的是实例对象
        print(food)
      
p1 = Person()
p2 = Person()

类其实就是自定义的数据类型,创建对象(实例化对象)的过程其实就是定义变量过程。

通过一个普通的类可以创建无数个对象,每个对象在内存中都会开辟新的空间。

调用类中的实例函数

方式一:使用 对象名.方法名(参数) 调用的方式,不需要传递self。

方式二:使用 类名.方法名(self, 参数) 的方式,不会自动给 self 传参,需要手动的指定self。

class Person(object):
    def eat(self, food):  # 实例方法有一个参数self,指的是实例对象
        print(food)


p1 = Person()

# 方式一
p1.eat('蘸水面')

# 方式二
Person.eat(p1, '蘸水面')

动态绑定属性和限制绑定

动态绑定属性

语法:对象.属性 = 初始值

class Person(object):
    def eat(self, food):  # 实例方法有一个参数self,指的是实例对象
        print(food)


p1 = Person()
p1.name = 'Lucy'
p1.age = 18

缺点:绑定的属性的数量没有上限,绑定的属性的名称没有限制,在实际项目开发中,需要进行限制绑定。

限定绑定

语法:__slots__ = (属性1, 属性2,…………)

class Person(object):
    __slots__ = ('name', 'age')

    def eat(self, food):  # 实例方法有一个参数self,指的是实例对象
        print(food)


p1 = Person()
p1.name = 'Lucy'
p1.age = 18
# p1.height = 178  # AttributeError: 'Person' object has no attribute 'height'

内存中的对象

class Person(object):
    num = 10

p11 = Person()
p12 = Person()
# p11.num 和 p12.num访问的是同一个num,num来自于类属性,当前类持有,通过该类创建的所有的对象都持有
print(p11.num is p12.num)  # True
print(p11.num, p12.num)  # 10 10
class Person(object):
    num = 10

p13 = Person()
p13.num = 20
p14 = Person()
p14.num = 20
# p13.num和 p14.num访问的不是同一个num,num被称为对象属性,仅限当前对象持有
print(p13.num is p14.num)  # True
del p13.num
print(p13.num, p14.num)  # 10 20

构造函数

在代码比较复杂的情况下,使用动态绑定属性的方式创建对象,就不太好了。这时需要一个名称为__init__ 的方法,该特殊的方法被称为构造函数,主要用于创建对象并将对象的数据初始化。

构造函数,也被称为构造器,指的是当创建对象的时候,被自动调用的函数。

严格意义讲,__new__ 不属于构造函数。

工作原理
class Book(object):
    # cls和self都不是关键字,可以是一个普通的标识符
    # cls全称class,表示当前类
    # __new__属于类方法
    def __new__(cls, *args, **kwargs):
        print("new被调用了")
        # 注意:__new__必须有返回值,并且返回一个实例
        # super(Book,cls).__new__(cls)表示当前类创建出来的一个实例
        return super(Book, cls).__new__(cls)

    # self表示当前对象
    # __init__属于实例函数
    def __init__(self):
        print("init被调用了")


b1 = Book()
print(b1)  # <__main__.Book  objcet at 0x6572846>
# 如果类中重写__str__,则不打印地址
工作原理:
1. __new__:创建对象的时候首先自动调用__new__,它的作用就是创建实例,然后将该实例返回
2. __init__:当实例创建完毕之后被调用的,然后通过__init__给实例设置初始值
3. __new__先被调用,__init__后被调用,__new__的返回值(实例)将传递给__init__的第一个参数self,然后__init__给这个实例设置一些参数,__init__不需要返回值。
__init__() 设置参数
class Student(object):
    __slots__ = ("name", "age", "hobby", "score")

    def __init__(self, a, b, c):
        self.name = a
        self.age = b
        self.hobby = c
        self.score = 60

但是,一般形参列表中变量的名称和规定的属性名一致,所以建议使用下面的书写方式。

class Student(object):
    __slots__ = ("name", "age", "hobby", "score")

    def __init__(self, name, age, hobby):
        self.name = name
        self.age = age
        self.hobby = hobby
        self.score = 60

析构函数

当对象被销毁的时候自动调用的函数,称为析构函数,析构函数为 __del__()

1. 将对象定义为全局变量,程序执行完毕,对象自动被销毁
class Animal(object):
    def __init__(self):
        print("init被调用了")

    def __del__(self):
        print("del被调用了")


a1 = Animal()
print("over")

"""
init被调用了
over
del被调用了
"""
2. 将对象定义为局部变量,当指定的函数执行完毕,则对象随着会被自动销毁
class Animal(object):
    def __init__(self):
        print("init被调用了")

    def __del__(self):
        print("del被调用了")


def func():
    a2 = Animal()
    print("函数内部")


func()
print("over")

"""
init被调用了
函数内部
del被调用了
over
"""
3. 强制销毁对象,什么时候del对象,则什么时候执行析构函数 __del__()
class Animal(object):
    def __init__(self):
        print("init被调用了")

    def __del__(self):
        print("del被调用了")


a3 = Animal()
del a3
print("over")

"""
init被调用了
del被调用了
over
"""

五、封装

概念

广义的封装:函数的定义和类的提取,都是封装的体现。

狭义的封装:在面向对象编程中,一个类的某些属性,在使用的过程中,如果不希望被外界直接访问,就可以将该属性封装(将不希望被外界直接访问的属性私有化private,该属性只能被当前类持有,此时可以给外界暴露一个访问的函数即可)。

封装的本质:就是属性私有化的过程。

封装的好处:提高了数据的安全性,提高了数据的复用性。

私有属性

1. 概念

公开属性:可以在类以外的任何地方直接访问。

私有属性:只能在类的内部被直接访问,在对象属性的前面添加两个下划线表示是私有属性。

2. 属性未被私有化
class Person(object):
    def __init__(self, name, age):
        # 公开属性
        self.name = name
        self.age = age


p1 = Person("jack", 10)
# 直接访问
print(p1.name, p1.age)  # jack 10

# 修改
p1.name = "tom"
p1.age = 19
print(p1.name, p1.age)  # tom 19
3. 属性被私有化

工作原理:一个属性一旦被私有化,在底层形成了 _类名__属性名 的属性名 ,但是不建议使用。私有化属性在底层的存在形式根据操作系统或者Python解释器的不同会有所差别,如果直接使用此种方式访问,违背了Python跨平台的特点。

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


p = Person("jack", 10)
# print(p.name,p.age) #AttributeError: 'Person2' object has no attribute 'name'
# print(p.__name) #AttributeError: 'Person2' object has no attribute '__name'
print(p._Person__name)  # jack
4. 暴露给外界访问的函数

在类外面访问属性,无非涉及到两个操作:获取值,修改值。

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

    # 返回被私有化属性的值
    def getname(self):
        return self.__name

    def setname(self, name):
        self.__name = name

    def getage(self):
        return self.__age

    def setage(self, age):
        if age < 0:
            age = abs(age)
        self.__age = age


p = Person("jack", 10)
# 获取值
r1 = p.getname()
print(r1)  # jack
# 修改值
p.setname("tom")
r2 = p.getname()
print(r2)  # tom

p.setage(-19)
print(p.getage())  # 19

5. @property装饰器
  1. 单独使用

@property 将一个函数转换为属性调用,为了简化函数的调用,函数名本身表示指定函数的返回值,如:show()使用@property修饰,则 show() 的调用:对象名.show

注意:如果函数被@property修饰,最好设置返回值。

class Check(object):
    @property
    def show(self):
        # 如果函数被@property修饰,最好设置返回值
        return 'hello'


c = Check()
# c.show()  #TypeError: 'str' object is not callable
print(c.show)  # hello
  1. @property@xxx.setter

为了简化函数的调用,Python中通过 @property@xxx.setter 分别修改两个函数。
注意:

  • 两个函数的函数名尽量和被私有化的属性名保持一致
  • @xxx.setter 中的 xxx 需要和被 @property 修饰的函数名保持一致
  • @property 修饰的函数用于获取值,@xxx.setter 修饰的函数用于修改值
  • @property@xxx.setter 并不是需要同时出现,根据自己的需求进行选择
    • @property出现,@xxx.setter 可以不出现
    • @xxx.setter出现,@property 必须出现
class Person(object):
    def __init__(self, name, age):
        self.__name = name
        self.__age = age

    # 获取值:将被私有化属性的值返回
    @property
    def name(self):
        return self.__name

    # 修改值:设置参数
    @name.setter
    def name(self, name):
        self.__name = name

    @property
    def age(self):
        return self.__age

    @age.setter
    def age(self, age):
        if age < 0:
            age = abs(age)
        self.__age = age


# 将函数当做属性访问
p = Person("tom", 17)
print(p.name)  # tom
p.name = "bob"
print(p.name)  # bob

私有函数

在类中,对象函数之间相互调用,语法:self.函数名

1. 函数未被私有化
class Person1(object):
    def func1(self):
        print("111")
        # 注意:在类中,对象函数之间相互调用,语法:self.函数
        self.func2()

    def func2(self):
        print("222")

    def show(self):
        print("show")


p1 = Person1()
p1.func1()
# 111
# 222
p1.show()
# show
2. 函数被私有化
class Person2(object):
    def func1(self):
        print("111")
        # 间接调用
        self.__show()

    def func2(self):
        print("222")

    def __show(self):
        print("show")


p2 = Person2()
p2.func2()  # 222
# p2.show()  # 报错
# p2.__show()  # 报错
# p2._Person2__show()  # show
p2.func1()
# 111
# 222

六、继承

如果两个或者两个以上的类具有相同的属性和方法,我们可以抽取一个类出来,在抽取出来的类中声明各个类公共的部分。被抽取出来的类称之为父类(超类、基类),两个或两个以上的类称之为子类 (派生类),他们之间的关系是 子类继承自父类 或者 父类派生了子类

单继承

1. 基本使用

子类之继承一个父类,被称为单继承。

语法:

class 子类类名(父类类名):
	类体

注意:object 是 Python 中所有类的根类。

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

    def eat(self):
        print("eating")

    def __show(self):
        print("showing")

  1. 在子类中未定义构造函数,则创建子类对象默认调用父类中的构造函数
class Doctor(Person):
    pass


d = Doctor("张医生", 30, "male")

# 注意:子类对象可以直接调用父类中未被私有化的函数
d.eat()  # eating
d._Person__show()  # showing 不建议这样使用
d._Doctor__show()  # AttributeError: 'Doctor' object has no attribute '_Doctor__show'

  1. 在子类中定义构造函数,则创建子类对象调用的是子类中的构造函数
# 有参数时
class Teacher(Person):
    def __init__(self, height):
        self.height = height


t = Teacher(180)
print(t.height)  # 180
# print(t.name, t.age, t.gender)  # AttributeError: 'Teacher' object has no attribute 'name'
t.eat()  # eating



# 无参数时
class Lawyer(Person):
    def __init__(self):
        print("lawyer~~~init")


la = Lawyer()  # lawyer~~~init

# print(la.name)  # AttributeError: 'Lawyer' object has no attribute 'name'
la.eat()  # eating

  1. 在子类的构造函数中调用父类的构造函数
方式一:
    super(当前类, self).__init__(参数列表)

方式二:
    super().__init__(参数列表)

方式三:
    父类名.__init__(self, 参数列表)

注意:在单继承中,三种方式都可以使用;在多继承中,只能使用方式三

class Worker(Person):
    def __init__(self, name, age, gender, type):
        super(Worker, self).__init__(name, age, gender)
        self.type = type

    def work(self):
        print("working")


w = Worker("工作者", 10, "男", "行政")
print(w.type)  # 行政
print(w.name, w.age, w.gender)  # 工作者 10 男

2. 继承中的 __slots__ 属性
  • __slots__ 定义的属性仅对当前类的实例起作用,对继承的子类实例是不起作用
class Aniaml(object):
    __slots__ = ("name", "age")


class Cat(Aniaml):
    pass


c = Cat()
c.name = "tom"
c.age = 6
c.num = 8
print(c.name, c.age, c.numPython面向对象之访问控制

python学习笔记之——python面向对象

Python学习笔记-面向对象进阶

Python中面向对象(学习笔记)

python学习笔记--面向对象的编程和类

Python 学习笔记 - 面向对象(类成员)