面向对象编程(OOP)

什么是面向对象 ?

面向对象编程 (OOPObject Oriented Programming)可以理解为将具有相互 关系的数据/操作封装成对象以对象的⻆度去处理问题让对象来完成相应处理 。

面向对象 vs 面向过程:

  • 按照业务逻辑 从上到下 设计程序 的⽅式,叫做 ⾯向过程编程 (Procedure OrientedProgrammingPOP,⾯向过程程序设计)
  • 面向过程编程 最易被初学者接受,其往往⽤⼀⻓段代码来实现指定功能,⼀步接⼀步,环环相扣
  • 对于⼤型项⽬,如果使⽤⾯向过程编程,则很容易导致 代码结构过于紧密、耦合度⾼易出现代码冗余,并且 不利于团队开发

⾯向对象编程 的优点 :

  • 将数据和业务抽象为 对象,有利于程序整体结构的分析和设计,使设计思路更加清晰
  • 业务以对象为单位,对象各⾃完成⼯作,减少了代码耦合度,有助于业务升级 和 代码重构
  • ⽅便⼯作划分,有利于提⾼团队开发的效率

类和对象

对象  是承载数据,执⾏操作的 ⼀个具体 事物,⽐如具体某⼀个⼈,具体某⼀只狗...

对象中包含两个组成部分:

  • 属性: 记录与对象相关的数据,⽐如姓名,年龄,身⾼,肤⾊等
  • ⽅法: 实现与对象相关的操作,⽐如吃饭,睡觉,飞行,歌唱等

   物以类聚,⼈以群分

  • 很多事物存在相同的操作/⾏为,⽐如⼈都进⾏吃饭、睡觉,狗都会跑会叫等等
  • 描述共同⾏为的集合,称为(class)
  • 类是总结事物特征的抽象概念,⽽对象是 具体存在的某个实物

类和对象的关系 :

  • 在编程中,类就是创建对象的制造⼿册,⽤来定义对象的⾏为**
  • 每个对象必须有⼀个对应的类

定义类

定义⼀个类,格式如下:

class 类名:
    ⽅法列表 

类名的命名规则按照"⼤驼峰"

定义方法

类是定义对象的共同⾏为的,也就是说在类中定义对象的⽅法

class 类名:
    def ⽅法名(self):
        ... 

⽅法的格式和函数类似,也可以设置参数和返回值,但是 需要设置第⼀个参数为 self

demo:定义⼀个Dog

# 定义类
class Dog:
    # ⽅法
    def eat(self):
        print("吃⻣头")

    def drink(self):
        print("喝⽔") 

创建对象

python中,可以根据已经定义的类去创建出对象

创建对象的格式为:

  • 引⽤对象的变量名 = 类名()
# 定义⼀个类
class Dog:
    # ⽅法  
    def eat(self):
        print("吃⻣头")

    def drink(self):
        print("喝⽔")

# 根据类,创建⼀个对象
dog1 = Dog() 

调用方法

创建对象的格式为:

  • 引⽤对象的变量名.⽅法名()

注意:虽然定义⽅法时设置第⼀个参数 self ,但是调⽤⽅法时不要传递对应 self 的参数

对象调⽤⽅法demo:

# 定义⼀个类
class Dog:
    # ⽅法
    def eat(self):
        print("吃⻣头")

    def drink(self):
        print("喝⽔")


# 根据类,创建⼀个对象
dog1 = Dog()
dog1.eat()   # 调用eat方法
dog2.drink()  # 调用drink方法

定义/使⽤属性

格式:

  • 引⽤对象的变量名.属性名 = 数据

属性和变量类似,⾸次赋值时会定义属性

# 创建对象
jiqimao = Cat()

# ⾸次赋值属性,会定义属性
jiqimao.age = 3

# 定义属性后,再赋值属性,会修改属性保存的数据
jiqimao.age = 1

# 获取属性值并打印
print(jiqimao.age)   # 1

创建多个对象

类作为对象的模具,根据类可以创建多个对象

# 定义类
class Cat:
    def eat(self):
        print("吃东⻄")

# 创建对象
cat1 = Cat()

# 调⽤⽅法
cat1.eat()

# 定义属性
cat1.name = "加菲"
print(cat1.name)

# ⼀个类是可以创建多个对象
cat2 = Cat()

# 调⽤⽅法
cat2.eat() # 同类对象都可以调⽤类中定义的对象⽅法

# 定义属性
# 同类的对象可以有同名的属性, ⽽且属性的值可以不同
cat2.name = "小叮当"
print(cat2.name) 

self 关键字

关键字 self 主要⽤于对象⽅法中,表示调⽤该⽅法的对象 。

  • 在⽅法中使⽤ self ,可以获取到调⽤当前⽅法的对象,进⽽获取到该对象的属性和⽅法
  • 某个对象调⽤其⽅法时,python解释器会把这个对象作为第⼀个参数传递给⽅法,所以开发者只需要在定义⽅法时 预留第⼀个参数为 self 即可,即不需要设置self对应的实参:
class Dog:
    def introduce(self): # self对应的实参 就是 调⽤该⽅法的对象
        # self 主要⽤于 在⽅法中使⽤⾃⼰的属性和⽅法
        print("我是%s, 请多多关照" % self.name)


dog1 = Dog()
dog1.name = "大黄"
dog1.introduce() # python解释器会⾃动将调⽤⽅法的对象作为第⼀个实参进⾏传递

# 创建另⼀个对象
dog2 = Dog()
dog2.name = "小黑"
dog2.introduce()   # 我是小黑,请多多关照

 

__init__()⽅法 

__init__() : 对象的初始化⽅法,在 创建⼀个对象后会被⾃动调⽤,不需要⼿动调⽤ 。

开发者可以 实现init⽅法,并在该⽅法中定义属性并设置初始值

class Dog:
    # 初始化⽅法
    def __init__(self):
        # 设置对象的默认属性及初始值
        self.type = "野狗"

# 创建了⼀个对象
dahuang = Dog( )

# 获取对象的属性
print(dahuang.type)   # 野狗

__init__( ) ⾃定义参数

  • 除了默认参数 self ,还可以设置任意个数的⾃定义参数,例__init__(self,x,y,z)
  • init⽅法的⾃定义参数必须和创建对象时传递的参数保持⼀致,例如 tom = Cat(x,y,z)
  • 开发者可以 设置⾃定义参数,为对象的默认属性提供 不同的初始值
class Dog:
    def __init__(self, name):
        # 如果希望属性有不同的初始值,可以设置init的⾃定义参数
        self.name = name

    def introduce(self):
        print("我是%s, 请多多关照" % self.name)

dog1 = Dog("大黄") # init对应的实参在创建对象时进⾏设置
dog1.introduce()

dog2 = Dog("小黑")
dog2.introduce() 

 

__del__() ⽅法

将要删除⼀个对象时,python解释器会⾃动调⽤⼀个⽅法,这个⽅法为 __del__() ⽅法

del⽅法可以辅助查看对象的删除情况

class Dog:
    # 将要删除对象时,会⾃动调⽤
    def __del__(self):
        print("对象将要被删除")

"""情况1"""
dog1 = Dog()
print("程序最后⼀⾏")

"""情况2"""
def func():
    dog1 = Dog()

func()
print("程序最后⼀⾏") 

 

__str__()⽅法

  • 如果直接print打印对象,会看到创建出来的对象在内存中的地址
  • 当使⽤ printxx输出对象的时候,只要对象定义了 __str__(self) ⽅法,就会打印该⽅法 return的信息描述
  • str⽅法的返回值必须是 字符串类型
class Cat:
    """定义⼀个猫类"""
    def __init__(self, new_name, new_age):
            self.name = new_name
            self.age = new_age

    def __str__(self):
            """返回⼀个对象的描述信息"""
            return "名字是:%s , 年龄是:%d" % (self.name, self.age)

# 创建了⼀个对象
tom = Cat("机器猫", 3)
print(tom) 

私有

私有属性 :如果在属性名前⾯加了2个下划线'__',则表明该属性是私有属性,否则为公有属性

                   私有属性只能在类的内部访问

class Dog:
    def __init__(self):
        # 私有属性: __xx 不能在类的外部使⽤
        self.__children_count = 0

    def birth(self):
        """⽣育"""
        print("⽣了⼀个崽⼉")
        # __children_count只有在调⽤birth⽅法时才能修改,不应该在类外部直接使⽤
        self.__children_count += 1

    def __str__(self):
        return "我有%d个崽⼉:%d" % self.__children_count

dog1 = Dog()
dog1.birth()
print(dog1)
dog1.birth()
print(dog1) 

私有⽅法 :私有⽅法和私有属性类似,在⽅法名前⾯加了2个下划线'__',则表明该⽅法是私有⽅法

                   私有⽅法只能在类内部使⽤
 

class Dog:
    def __init__(self):
        # 私有属性: __xx 不能在类的外部使⽤
        self.__children_count = 0

    def birth(self):
        """⽣育"""
        print("⽣了⼀个崽⼉")
        self.__children_count += 1
        # 休产假
        self.__birth_leave() # 休产假只有在⽣孩⼦⽅法中才能使⽤,不能在类外部直接调⽤

    def __birth_leave(self):
        """产假"""
        print("休3个⽉产假")

    def __str__(self):
        return "我有%d个崽⼉:%d" % self.__children_count

dog1 = Dog()
dog1.birth()
print(dog1)
dog1.birth()
print(dog1) 

继承

⻰⽣⻰,凤⽣凤,⽼⿏的⼉⼦会打洞

面向对象三⼤特性: 封装 继承 多态

继承:某个类直接具备另⼀个类的能⼒(属性和⽅法)

格式:class ⼦类名:(⽗类名)

class Animal:
    def eat(self):
        print("-----吃-----")

    def drink(self):
        print("-----喝-----")

class Dog(Animal):
    """
   def eat(self):
        print("-----吃-----")

    def drink(self):
        print("-----喝-----")
    """
    pass

xiao_hei = Dog()
xiao_hei.eat()
xiao_hei.drink() 

多层继承

继承关系可以在多层间传递

class Animal:
    def eat(self):
        print("-----吃-----")

    def drink(self):
        print("-----喝-----")

class Dog(Animal):
    def bark(self):
        print("-----汪汪叫------")

class DYST(Dog):
    """定义了⼀个地狱三头犬类"""
    pass

class Cat(Animal):
    def catch(self):
        print("----捉⽼⿏----")

dyd = SYST()
dyd.eat()
dyd.bark()

 

重写⽗类⽅法

当⼦类实现⼀个和⽗类同名的⽅法时,叫做重写⽗类⽅法

⼦类重写了⽗类⽅法,⼦类再调⽤该⽅法将不会执⾏⽗类的处理

class Animal:
    def eat(self):
        print("-----吃-----")
    def drink(self):
        print("-----喝-----")

class Dog(Animal):
    def bark(self):
        print("-----汪汪叫------")

class DYST(Dog):
    """定义了⼀个地狱三头犬类"""
    def bark(self):
        print("----嘻嘻嘻嘻-----")
dyd = DYST()
dyd.eat()   # ----吃----
dyd.bark()  # ----嘻嘻嘻嘻----

调⽤被重写的⽗类⽅法

⼦类重写了⽗类⽅法,仍然想执⾏⽗类中的⽅法,则可以在类中使⽤ super() 来调⽤⽅法

class Dog:
    def bark(self):
        print("-----汪汪叫------")

class DYST(Dog):
    """定义了⼀个地狱三头犬类"""
    def bark(self):
        print("----滚-----")

    def see_host(self):
        """看⻅主⼈"""
        print("冷漠脸")

# 调⽤已经被重写的⽅法 三种⽅式

Dog.bark(self) # ⽅式1

super(DYST, self).bark() # ⽅式2

super().bark() # ⽅式3 ⽅式2的简化形式
dyq = DYST()
dyq.see_host() # 输出: 冷漠脸 汪汪叫

私有和继承

⽗类中的 私有⽅法、属性,不会被⼦类继承

可以通过调⽤继承的⽗类的共有⽅法,间接的访问⽗类的私有⽅法、属性

class Animal(object):
    def __init__(self):
        self.num1 = 1
        self.__num2 = 2
    
    def __run(self):
        print("----跑---")

    def eat(self):
        print("-----吃-----")

    def drink(self):
        print("-----喝-----")

    def test(self):
        print(self.__num2)
        self.__run()

class Dog(Animal):
    def bark(self):
        print("-----汪汪叫------")
        # self.__run() # ⽗类中的私有⽅法,没有被⼦类继承
        print(self.num1)
        # print(self.__num2) # ⽗类中的私有属性,没有被⼦类继承


xiao_hei = Dog()
xiao_hei.bark()
xiao_hei.test() 

多继承

从图中能够看出,所谓多继承,即⼦类有多个⽗类,并且具有它们的特征

class Basketball:
    def bark(self):
        print("gun")

class Chicken:
    def sing(self):
        print("鸡你太美")

class Kun(Basketball, Chicken): # 多继承使⽤逗号隔开
    pass

lianxisheng = Kun()
lianxisheng.bark()   # gun
lianxisheng.sing()   # 鸡你太美

注意点:

如果在上⾯的多继承例⼦中,如果⽗类篮球和⽗类中,有⼀个同名的⽅法,那么通过⼦类去调⽤的时候,调⽤哪个?

  • 后者覆盖前者
class Basketball:
    def eat(self):
        print("东南西北风")

class Chicken:
    def eat(self):
        print("饲料")

class LXS(Basketball, Chicken):
    # 在多继承中,调⽤指定⽗类的⽅法
    def eat(self):
        Chicken.eat(self) # 调⽤God的eat⽅法

zhangsan = LXS()
print(LXS.__mro__) # 多继承中使⽤ mro变量来查看继承查询顺序
zhangsan.eat() # 饲料

多态

多态的概念是应⽤于JavaC#这⼀类强类型语⾔中,⽽Python崇尚鸭⼦类型

所谓多态:定义时的类型和运⾏时的类型不⼀样,此时就成为多态。

Python伪代码实现JavaC#的多态

#-*- charset ='utf-8' -*-
class F1(object):
    def show(self):
        print 'F1.show'

class S1(F1):
    def show(self):
        print 'S1.show'

class S2(F1):
    def show(self):
        print 'S2.show'

# 由于在Java或C#中定义函数参数时,必须指定参数的类型
# 为了让Func函数既能执⾏S1对象的show⽅法,⼜能执⾏S2对象的show⽅法,所以定义了⼀个S1和S2类的⽗类
# 实际传⼊的参数是:S1对象和S2对象

def Func(F1 obj):
    """Func函数需要接收⼀个F1类型或者F1⼦类的类型"""
    print obj.show()
    
s1_obj = S1()
Func(s1_obj) # 在Func函数中传⼊S1类的对象 s1_obj,执⾏ S1 的show⽅法,结果:S1.show

s2_obj = S2()
Func(s2_obj) # 在Func函数中传⼊Ss类的对象 ss_obj,执⾏ Ss 的show⽅法,结果:S2.show 

Python “鸭⼦类型

鸭⼦类型语⾔中,函数/⽅法可以接受⼀个任意类型的对象作为参数/返回值,只要该对象实现了代码后续⽤到的属性和⽅法就不会报错 。

class F1(object):
    def show(self):
        print 'F1.show'

class S1(F1):
    def show(self):
        print 'S1.show'

class S2(F1):
    def show(self):
        print 'S2.show'

    def Func(obj):
        print obj.show()

s1_obj = S1()
Func(s1_obj)

s2_obj = S2()
Func(s2_obj) 

实例属性、类属性

实例属性:

通过类创建的对象⼜称为实例对象对象属性⼜称为实例属性,记录对象各⾃的数据,不同对象的同名实例属性,记录的数据可能各不相同。

类属性:

  • 类属性就是类对象所拥有的属性,它被该类的所有实例对象所共有
  • 类属性可以使⽤类对象实例对象访问 。
class Dog:
    type = "狗" # 类属性

dog1 = Dog()
dog1.name = "大黄"
dog2 = Dog()
dog2.name = "小黑"

# 类属性 取值
print(Dog.type) # 结果:狗
print(dog1.type) # 结果:狗
print(dog2.type) # 结果:狗
提示:在python中 “万物皆对象”,类本身也是⼀个对象,执⾏class语句时会被创建,称为类对象。

使⽤场景:

  • 类的实例:记录的某项数据始终保持⼀致时,则定义类属性。
  • 实例属性:要求每个对象为其单独开辟⼀份内存空间来记录数据,⽽类属性为全类所共有 ,占⽤⼀份内存更加节省内存空间。 

注意点:

类属性只能通过类对象修改,不能通过实例对象修改

class Dog(object):
    type = "狗" # 类属性

# 创建对象
dog1 = Dog()
Dog.type = "Dog" # 使⽤ 类对象 修改类属性
dog1.type = "dog" # 不是修改类属性, ⽽是创建了同名的对象属性type
print(Dog.type) # 结果为 "Dog" 类对象访问类属性
print(dog1.type) # 结果为 “dog” 类属性和对象属性同名,实例对象优先访问对象属性 

类属性也可以设置为 私有,前边添加两个下划线。 如:

class Dog(object):
    count = 0 # 公有的类属性
    __type = "狗" # 私有的类属性

print(Dog.count) # 正确
print(Dog.__type) # 错误 

类方法、静态方法

类⽅法类对象所拥有的⽅法

需要⽤装饰器 @classmethod 来标识其为类⽅法,对于类⽅法,第⼀个参数必须是类对象,⼀般以 cls 作为第⼀个参数。

class Dog(object):
    __type = "狗"

    # 类⽅法,⽤classmethod来进⾏修饰
    @classmethod
    def get_type(cls):
        return cls.__type

print(Dog.get_type()) 

使⽤场景:

  • 当⽅法中需要使⽤类对象 (如访问私有类属性等)时,定义类⽅法
  • 类⽅法⼀般和类属性配合使⽤

静态方法

需要通过装饰器 @staticmethod 来进⾏修饰,静态⽅法既不需要传递类对象也不需要传递实例对象(形参没有self/cls

静态⽅法也能够通过实例对象类对象去访问。

class Dog(object):
    type = "狗"
    def __init__(self):
        name = None

    # 静态⽅法
    @staticmethod
    def introduce(): # 静态⽅法不会⾃动传递实例对象和类对象
        print("⽝科哺乳动物,属于⻝⾁⽬..")

dog1 = Dog()
Dog.introduce() # 可以⽤ 实例对象 来调⽤ 静态⽅法
dog1.introduce() # 可以⽤ 类对象 来调⽤ 静态⽅法

使⽤场景:

当⽅法中既不需要使⽤实例对象(如实例对象,实例属性)也不需要使⽤类对象 (如类属性、类方法、创建实例等)时,定义静态⽅法 。

取消不需要的参数传递,有利于减少不必要的内存占⽤和性能消耗 。

注意点:

类中定义了同名的对象⽅法、类⽅法、静态⽅法时,调⽤⽅法会优先执⾏最后定义的⽅法 。

class Dog:
    def demo_method(self):
        print("对象⽅法")

    @classmethod
    def demo_method(cls):
        print("类⽅法")

    @staticmethod
    def demo_method(): # 被最后定义,调⽤时优先执⾏
        print("静态⽅法")

dog1 = Dog()
Dog.demo_method() # 结果: 静态⽅法
dog1.demo_method() # 结果: 静态⽅法 

 

__new__ ⽅法

创建对象时,系统会⾃动调⽤new⽅法

开发者可以实现new⽅法来⾃定义对象的创建过程

class Cat:
    def __new__(cls):
        print("创建对象")
        return object.__new__(cls)

    def __str__(self):
        return "%s" % self.name

dog1 = Dog()
dog1.age = 20
print(dog1) 

总结

  • __new__ ⾄少要有⼀个参数cls,代表要实例化的类,此参数在实例化时由Python解释器⾃动提供
  • __new__ 必须要有返回值,返回实例化出来的实例,这点在⾃⼰实现 __new__ 时要特别注意,可
  • return⽗类 __new__ 出来的实例,或者直接是object__new__ 出来的实例

关于python面向对象的基本要点到这里就结束了,了解更多见:https://blog.csdn.net/Scrat_Kong/article/details/90257118

 

猜你喜欢

转载自blog.csdn.net/Scrat_Kong/article/details/100034538