面向对象基础

初识面向对象

概述

  • 面向过程: 根据业务逻辑从上到下 垒代码
  • 函数式: 将某功能代码封装到函数中,日后便无需重复编写,直接调用函数即可。
  • 面向对象:将对象进行分类和封装。让开发更快更好更强

创建类和对象

面向对象编程(oop),是一种编程方式。此编程方式的落地需要使用’类‘和’对象‘来实现,所以,面向对象编程其实就是对’类‘和’对象‘的使用。

class 类名:
	属性 = 'a'
    
对象 = 类()

类名的作用就是 操作属性,查看属性。

类的定义

类就是一个模板,模板里可以包含多个函数,函数里实现一些功能。

对象则是根据模板创建的实例,通过实例对象可以执行类中的函数。

class Person:	# 类名
    country = 'China' # 创造了一个只要是这个类就一定有的属性。类属性,静态属性
    
    def __init__(self,*args):	#初始化方法。self是对象,是一个必须传的参数。
        #self就是一个可以存储很多属性的大字典。self拥有的属性都属于对象。
        self.name = 'nick'
    
    # 方法,一般情况下必须传self参数,且必须写在第一个
    # 后面还可以传其它参数,
    def walk(self,n):
        print(f'{self.name}走了{n}步')
        
print(Person.country)  # 类名 可以查看类中的属性,不需要实例化即可查看

nick = Person('nick') # 类名可以实例化对象。nick对象

print(nick.name) # 查看所有的属性,通过 . 点出

nick.walk() # Person.walk(nick,5) # 调用方法 类名.方法名(对象名)


示例

class Person:
    country = 'China'

    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex

    def walk(self,n):
        print(f'{self.name}都{self.age}岁了,还没走过{n}步。')

nick = Person('nick',5,'man')
nick.walk(1)
print(nick.name)
print(nick.age)
print(nick.sex)
  1. 定义一个类,必须class+类名:
  2. 类里可以定义静态属性,这个属性可以直接使用使用类名调用,也称类属性
  3. 类里可以定义动态属性,也就是方法。每个方法都有一个必须传的self属性。类调用这个方法时,需要自己传self,实例化后的对象会自动传self
  4. __init__方法是类的初始化方法。
    1. 实例化的时候,python帮我们创建了一个对象self
    2. 每当我们调用类的时候就会自动触发这个方法。默认传self
    3. 在init方法里面可以对self进行赋值
  5. self是什么。
    1. self就是一个大字典
    2. self拥有的属性都属于对象
    3. 在类的内部,self就是一个对象

实例化

对象 = 类名()

过程:

  1. 类名() 首先会创造出一个对象,创建了一个self变量
  2. 调用__init__方法,类名括号里的参数会被这里接收
  3. 执行__init__方法
  4. 返回self

对象能做什么

  1. 对象可以查看类中的属性,
  2. 可以调用类中所用的方法。
  3. __dict__ 对于对象的增删改查都可以通过字典的语法 进行

类名能做什么

  1. 实例化对象
  2. 调用方法:只不过要自己传递self参数
  3. 调用类中的属性,也就是调用静态属性
  4. __dict__ 对类中的名字只能看 不能操作
# 代码演示
# 人狗大战
class Dog:
    def __init__(self,name,blood,aggr,kind):
        self.name = name
        self.hp = blood
        self.aggr = aggr
        self.kind = kind
    def bite(self,person):
        # 狗咬人,人掉血
        person.blood -= self.aggr

class Person:
    def __init__(self,name,blood,aggr,sex):
        self.name = name
        self.blood = blood
        self.aggr = aggr
        self.sex = sex
    def attack(self,dog):
        dog.hp -= self.aggr
        if dog.hp <= 0:
            print('%s打了%s,%s被打死了,扑街~~~'%(self.name,dog.name,dog.name))
        else:
            print('%s打了%s,掉了%s血'%(self.name,dog.name,self.aggr))

jin = Dog('金老板',100,20,'teddy')
# print(jin.name)
alex = Person('alex',999,998,'不详')
jin.bite(alex)   # Dog.bite(jin,alex)
print(alex.blood)
# alex attack
alex.attack(jin)  # Person.attack(alex,jin)
print(jin.hp)

类和对象的命名空间

类里面我们可以定义两种属性:静态属性和动态属性。

类中的静态属性 可以被对象和类 调用。

  1. 对于不可变数据类型来说,类变量最好用类名操作
  2. 对于可变数据类型来说,对象名的修改是共享的,重新赋值是独立的。(对象名的修改,每个实例化后的对象也随着更改,重新赋值的话,只有本对象的数据被重新赋值,其他实例化出来的数据是不变的)

扩展

创建一个类,每实例化一个对象就计数,最终所有的对象共享这个数据。

class Foo:
    count = 0
    
    def __init__(self):
        count += 1
        
f1 = Foo()
f2 = Foo()
print(f1.count)
print(f2.count)
f2 = Foo()
print(f1.count)

绑定方法

class Foo:
    def func(self):
        pass
print(Foo.func) #<function Foo.func at 0x055AA4B0>

f1 = Foo()
print(f1.func) #<bound method Foo.func of <__main__.Foo object at 0x05519EB0>>

创建包的时候,会自动创建一个__init__.py文件

导入一个模块,就相当于类的实例化的过程

面向对象的三大特性

封装,继承,多态

封装

封装,顾名思义就是将内容封装到某个地方,以后再去调用被封装在某处的内容。对代码的保护,不让别人看见。

所以,在使用面向对象的封装特性时,需要

  • 将内容封装到某处
  • 从某处调用被封装的内容

第一步:将内容封装到某处

self是一个形参,当执行实例化时,self就是实例化后的对象,所以内容其实被封装到了对象中,每个对象中都有类的属性,在内存里面类似于下图来保存。

第二步:从某处调用被封装的内容

调用被封装的内容时,有两种情况:

  • 通过对象直接调用

  • 通过self间接调用

    class Foo:
      
        def __init__(self, name, age):
            self.name = name
            self.age = age
      
        def detail(self):
            print self.name
            print self.age
      
    obj1 = Foo('wupeiqi', 18)
    obj1.detail()  # Python默认会将obj1传给self参数,即:obj1.detail(obj1),所以,此时方法内部的 self = obj1,即:self.name 是 wupeiqi ;self.age 是 18
     
    

    总结:对于面向对象的封装来说,其实就是使用构造方法将内容封装到对象中,然后通过对象直接或self间接获取被封装的内容。

class Person:
    __key = 123 # 双下划线,表示私有。这里是私有静态属性
    def __init__(self,name,passwd):
        self.name= name
        self.__passwd= passwd  #私有属性
        
    def __get_pwd(self):    # 私有方法
        return self.__passwd # 只要在类的内部使用私有属性,就会自动的带上_类
    def login(self):
        return self.__get_pwd() # 正常的方法调用私有的方法
        
        
alex = Person('alex','alex3714')
print(alex._Person__passwd) # _类名__属性名

print(alex.get_pwd()) # 这种调用是错误的,正确方式如下
print(alex.login())  #要使用正常的方法调用私有的方法



# 所有的私有 都是在变量的左边加上双下划线
	# 对象的私有属性
    # 类中的私有方法
    # 类中的静态私有属性
# 所有的私有的,都不能在类的外部使用
    

继承

继承,即子类继承父类的内容。

所以,对于面向对象的继承来说,其实就是将多个类共有的方法提取到父类中,子类仅需继承父类而不必自己再一个一个的实现每个方法。

一个类可以被 多个类 继承,一个类可以继承多个类

被继承的类 称之为 父类、基类、超类

继承父类的类 称之为 子类,派生类

python3里都是 新式类,没有继承父类的类都默认继承object

父类中没有的属性,在子类中出现 ,叫做 派生属性

父类中没有的方法,在子类中出现,叫做 派生方法

只要是子类的对象调用,子类中有的名字一定是用子类的,子类中没有才找父类的。如果父类也没有这个名字,就报错。

如果父类中的方法和属性 子类中都有,那就用子类的。如果你仍然想用父类的,需要单独调用父类的这个方法或属性。

  1. 父类.方法名(self,...) 需要自己传self参数
  2. super().方法名 不需要自己传self---只有新式类中可以使用这种方法

正常的代码中, 使用单继承,可以减少代码的重复

继承表达的是一种 子类 是父类的关系。

注意:上述的查找过程中,一旦找到,则立即中断寻找过程,不会继续找下去了。

多态

python不支持Java和c# 这一类强类型语言中多态的写法,但是原生多态,其python崇尚’鸭子类型‘

class F1:
    pass


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) 

Python “鸭子类型”

总结

  • 面向对象是一种编程方式,此编程方式的实现是基于 对象的使用。
  • 类 是一个模板,模板中包装了多个’函数‘供使用。
  • 对象,根据模板创建的实例(即 对象),对象用于调用被包装在类中的函数
  • 面向对象的三大特性:封装、继承和 多态

组合的用法

一个对象的属性值 是另外一个类的对象。

# 组合
# 人狗大战
class Dog:
    def __init__(self,name,aggr,hp,kind):
        self.name = name
        self.aggr = aggr
        self.hp = hp
        self.kind = kind

    def bite(self,person):
        person.hp -= self.aggr

class Person:
    def __init__(self,name,aggr,hp,sex):
        self.name = name
        self.aggr = aggr
        self.hp = hp
        self.sex = sex
        self.money = 0

    def attack(self,dog):
        dog.hp -= self.aggr

    def get_weapon(self,weapon):
        if self.money >= weapon.price:
            self.money -= weapon.price
            self.weapon = weapon
            self.aggr += weapon.aggr
        else:
            print("余额不足,请先充值")

class Weapon:
    def __init__(self,name,aggr,njd,price):
        self.name = name
        self.aggr = aggr
        self.njd = njd
        self.price = price

    def hand18(self,person):
        if self.njd > 0:
            person.hp -= self.aggr * 2
            self.njd -= 1

alex = Person('alex',0.5,100,'不详')
jin = Dog('金老板',100,500,'teddy')
w = Weapon('打狗棒',100,3,998)
# alex装备打狗棒
alex.money += 1000
alex.get_weapon(w)
print(alex.weapon)
print(alex.aggr)
alex.attack(jin)
print(jin.hp)
alex.weapon.hand18(jin)
print(jin.hp)

# 组合 :一个对象的属性值是另外一个类的对象
#        alex.weapon 是 Weapon类的对象

继承

多继承

新式类中的继承顺序: 广度优先

经典类中的继承顺序是 : 深度优先,python2 版本

在单继承中,子类有的用子类的,没有就用父类的。

在多继承中,默认是就近原则,在经典类中是按照深度优先进行继承,而在新式类中是按照广度优先的顺序进行查找。

在python2中新式类和经典类是共存的,新式类要继承object。

在python3 只有新式类,默认继承object

经典类和新式类的区别

多继承寻找名字的顺序:新式类广度优先,经典类深度优先

新式类中有一个类名.mro 方法,查看广度优先的继承顺序

python3 中 有个super 方法,根据广度优先的继承顺序查找上一个类

super

只在python3 中存在,它的本质是: 不是单纯的找父类,而是根据调用者的节点位置的广度优先顺序来找名字的。

原文地址:https://www.cnblogs.com/chenych/p/10996429.html