python面向对象

面向对象编程

类的定义形式多样

  • 既可以直接创建新的类,也可以基于一个或多个已有的类创建新的类;
  • 既可以创建一个空的类,然后再动态添加属性和方法,也可以在创建类的同时设置属性和方法。
  • 类是对现实世界中一些事物的封装,可以把数据(属性)和操作(方法)封装在一起,从而使得
    程序结构更加清晰。
  • 使用class保留字定义一个类:

定义类

定义一个类

class Person:
    #定义一个属性
    name="小明"
    #定义一个方法
    def getName(self):
        return self.name

定义一个空类

class Person:
    pass

创建对象

p=Person()
  • 类定义好后进行实例化 p=Person( ),产生一个Person类的实例对象
  • 实例对象是根据类模版生成一个内存体,系统都会在内存中选择一块区域分配给对象,有确定的
    数据与内存地址,每次选择的内存通常是不一样的

构造函数

构造函数:初始化方法,命名为__init__,在对象创建后自动调用

class Person:
    name="小明"
    def __init__(self):
        print(self,self.name)


f=Person()

默认参数

class Person:
    def __init__(self,n="小红"):
        self.name=n
        print(self,self.name)

>>>f=Person()
>>>p=Person("小明")

自定义参数

class Complex:
    def __init__(self,realpart,imagpart):
        self.real=realpart
        self.imag=imagpart

类属性和实例属性

class Person:
    name="Max"# 类属性
    age=12#类属性
    def __init__(self,height):
        self.height=height#实例属性
  • 类属性可通过两种方式访问:
    • 使用类的名称,例如,Person.name, Person.age
    • 使用类的实例对象,例如p=Person( )是对象,通过p.name, p.age访问
    • 注意:类属性虽然归类所有,但实例化的对象都可以访问类属性
  • 实例属性通过实例对象可绑定属性,新建、改变属性

改变类属性

class Person:
    name='Max'
    age=10
#类型读取类属性
print(Person.name,Person.age)

#类名称改变类属性
Person.name="小明"
Person.age=22

print(Person.name,Person.age)

改变实例属性

通过对象实例改变类属性
格式:对象实例.属性=…

  • 如果该对象实例存在这个属性,这个属性的值就被改变。
  • 如果该对象实例不存在这个属性,自动为该对象实例创建一个新的属性。
class Person:
    name='Max'
    age=10

#读取类属性
print(Person.name,Person.age)

p=Person()
p.name='John'
print(p.name,p.age)
print(Person.name,Person.age)

# 'Max' 10
# 'John' 10
# 'Max' 10

相同名称的实例属性优先级高于类属性(覆盖)实例属性改变,不影响类属性

删除实例属性

删除实例属性后,访问该名称对应的属性时,将访问类属性

class Person:
    name='Max'
    age=12

p=Person()
print(p.name)#Max
p.name='John'
print(p.name)#John
print(Person.name)#Max
del p.name
print(p.name)#Max

不同实例对象属性不同

不同的实例对象,有不同的实例属性

class Person:
    name='Max'
    age=12
#用类名读取类属性
print(Person.name,Person.age)

# 用对象实例改变类属性
p=Person()
p.name='John'
print(p.name,p.age)
print(Person.name,Person.age)

q=Person()
q.age=21
print(q.name,q.age)

# Max 12
# 'John' 12
# Max 12
# Max 21

建立新的属性

class Person:
    name='Max'
    age=22

#对象实例改变类属性
p=Person()
p.gender='male'
print(p.name,p.age,p.gender)

Python作为一种动态语言,除了可以在定义类时指定类属性外,还可以动态地为已经创建的对象绑定新的属性

可变对象作为类属性

class Person:
    part_time=[]
    def __init__(self,name):
        self.name=name
    def add_part(self,sh):
        self.part_time.append(sh)

p=Person('Max')
q=Person('John')
q.add_part('read')
p.add_part('walk')

print(p.part_time)
print(q.part_time)
#['read','walk']
#['read','walk']
# part_time被共享了

使用实例属性替代类属性,以避免实例操作改变类属性变量的值

class Person:
    def __init__(self,name):
        self.name=name
        self.part_time=[]
    def add_part(self,sh):
        self.part_time.append(sh)

p=Person('Max')
q=Person('John')
q.add_part('read')
p.add_part('walk')
print(p.part_time)
print(q.part_time)
#['walk']
#['read']
#各是各的

小结

  • 类属性是与类绑定的,它被这个类所拥有的,如果要修改类的属性,就必须使用类名称访问它,而不能使用对象实例访问它。
  • 对象实例的属性默认和类相同,通过赋值语句进行修改、创建后,不同的实例对象,有不同的属性。
  • 可变对象作为类属性时,需注意每个实例操作都
    可能改变其值。

类方法

实例方法

通过实例对象调用的方法一般以‘self’的变量作为第一个参数(其他名称也可以),self表示对象自身的含义,某个实例对象调用该方法时,将该对象作为第一个参数传递给self

class Person:
    name='Max'
    age=12
    def getName(self):
        return self.name
    def getAge(self):
        return self.age
p=Person()
print(p.getName(),p.getAge())
#Max 12

p.getName( )时,将p传递给self,执行return p.name得 到name,类似地,通过p.getAge( )得到age

实例方法调用

  1. 一般使用实例对象调用,调用时向实例方法传递实例参数,例如p.getName( )
  2. 也可以用类名称Person调用,但要传递实例参数,例如Person.getName(p)
class Person:
    name='Max'
    age=12
    def getName(self):
        return self.name
    def getAge(self):
        return self.age
p=Person()
print(p.getName(),p.getAge())
print(Person.getName(p),Person.getAge(p))
#Max 12
#Max 12

类方法

定义

在类中可以定义类的方法

  • 通过@classmethod 修饰
  • 第一个参数一般命名为cls (也可以是其他名称)
class Person:
    name='Max'
    age=12
    @classmethod
    def show(cls):
        print(cls.name,cls.age)

调用

一般通过类名称调用,调用时传递类参数,例如Person.show( ), 将Person 传递给cls,因此
print(cls.name,cls.age)等价于执行print(Person.name,Person.age)

class Person:
    name='Max'
    age=12
    @classmethod
    def show(cls):
        print(cls.name,cls.age)

Person.show()

也可以通过实例对象调用,这时候对象p的类Person会传递给函数的参数cls

class Person:
    name='Max'
    age=12
    @classmethod
    def show(cls):
        print(cls.name,cls.age)

p=Person()
p.show()

静态方法

通过@staticmethod修饰,无参数传递

class Person:
    name='Max'
    age=12
    @staticmethod
    def display():
        print(Person.name,Person.age)

通过类名称调用,不传递参数,与类方法调用不同(传递类参数)

静态方法调用

通过类名称调用,不传递参数,与类方法调用不同(传递类参数)

class Person:
    name='Max'
    age=12
    @classmethod
    def show(cls):
        print(cls.name,cls.age)
    @staticmethod
    def display():
        print(Person.name,Person.age)
>>>Person.show()
>>>'Max' 12
>>>Person.display()
>>>'Max' 12

通过实例对象调用,不传递参数

class Person:
    name='Max'
    age=12
    
    @staticmethod
    def display():
        print(Person.name,Person.age)
p=Person()
p.display()
#Max 12

小结

  • 实例方法
    • self
    • 对象实例调用p.getName( ),类名称调用
    • (Person.getName(p)、传递对象实例参数
  • 类方法
    • @classmethod, cls
    • 类名称调用Person.show( )、对象实例调用 p.show( ),传递类参数
  • 静态方法
    • @staticmethod
    • 类名称调用Person.display ( )、对象实例调用p.display( ),不传递参数

访问权限

Person类中的name 和age属性是公有的,可以直接在类外访问

class Person:
    name='Max'
    age=12

私有变量

如果想定义为私有的,在属性前面加两个下划线‘__’

class Person:
    __name='Max'
    age=12

私有属性不能直接在类外访问

Person.name
#报错

私有属性,可以被类内定义的方法访问

class Person:
    __name='Max'
    age=12
    def getName(self):
        return self.__name

p=Person()
p.show()

私有方法

可以在类内定义私有方法,在方法名称前加两个下划线‘__’

class Person:
    __name='Max'
    age=12
    def __getName(self):
        return self.__name

私有方法不能直接在类外调用

class Person:
    __name='Max'
    age=12
    def __getName(self):
        return self.__name
p=Person()
name=p.getName()

私有方法可被类内定义的其它方法调用

class Person:
    __name='Max'
    age=12
    
    def __show(self):
        print(self.name)
    def display(self):
        self.__show()
p=Person()
p.display()

小结

  • 在共有属性/方法前加两个下划线‘__’,即变成私有属性/方法
  • 公有属性/方法可以在类外直接访问/调用
  • 私有属性/方法不可以再类外直接访问/调用
  • 在类定义的内部,可以访问私有属性/方法

继承

继承的语法形式

  • DerivedClassName为子类,BaseClassName为父类
    (又称基类、超类)
class DerivedClassName(BaseClassName):
    pass

子类通过继承可以获得父类的所有方法

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

class Student(Person):
    pass

p=Student('Max')
print(p.name)
print(p.getName())

重写

对于父类的方法,子类实现重新定义

class Person:
    def __init__(self,name):
        self.name=name
    def read(self):
        print('The Person '+self.name+' is reading...')


class Student(Person):
    def read(self):
        print('The Student'+self.name+' is reading...')

p=Person('xiaoming')
p.read()
>>>The Person xiaoming is reading...
q=Student('xiaohong')
q.read()
>>>The Student xiaohong is reading...

若不想子类覆盖基类的方法,可将方法设为私有

class Person:
    def __init__(self,name):
        self.name=name
    def __read(self):
        print('The Person '+self.name+' is reading...')
    def test(self):
        self.__read()


class Student(Person):
    def __read(self):
        print('The Student'+self.name+' is reading...')

p=Person('xiaoming')
p.test()
>>>The Person xiaoming is reading...
q=Student('xiaohong')
q.test()
>>>The Peerson xiaohong is reading...

重写构造方法

class Person:
    def __init__(self,name):
        self.name=name
    def read(self):
        print('The Person '+self.name+' is reading...')


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

>>>p=Student(12)
>>>p.age
>>>12
>>>p.name
>>>报错,没有name这个属性

  1. 调用父类的构造函数
class Person:
    def __init__(self,name):
        self.name=name
    def read(self):
        print('The Person '+self.name+' is reading...')


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

>>>p=Student('Hello',12)
>>>p.name
>>>'Hello'
>>>p.age
>>>12
  1. 使用super函数
class Person:
    def __init__(self,name):
        self.name=name
    def read(self):
        print('The Person '+self.name+' is reading...')


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

>>>p=Student('Hello',12)
>>>p.name
>>>'Hello'
>>>p.age
>>>12

子类中添加新方法

class Person:
    def __init__(self,name):
        self.name=name
    def read(self):
        print('The Person '+self.name+' is reading...')


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

    def getAge(self):
        return self.age

>>>p=Student('xiaoming',12)
>>>print(p.getAge())
>>>'xiaoming'

两个重要的内置方法

  • isinstance(object,classinfo)如object是classinfo的实例返回True或object是classinfo子类的实例返回True
  • issubclass(class,classinfo)如class是classinfo的子类返回True

在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类

isinstance(p,Student)#True
isinstance(p,Person)#True


issubclass(Student,Person)#True
issubclass(Person,Student)#False

多继承

class DerivedClassName(Base1,Base2,Base2):
    pass

继承也可一级级继承下来,而任何类,最终都可以回溯到根节点object,这些继承关系就像一颗倒挂的树。

子类把父类的所有功能都直接拿过来,这样就不必从零做起

  • 子类把父类不适合的方法覆盖(构造函数的重写)
  • 若不想子类覆盖父类的方法,将父类方法设为私有
    方法
  • 子类可新增自己特有的方法

多态

多态:呈现多种形态

  • 尽管不知道变量指向的是哪种类型的对象,也能对其进行操作,且操作的行为会随对象所属的类型(类)而异
class Person:
    def __init__(self,name):
        self.name=name
    def getName(self):
        return self.name
    def read(self):
        print('The person is '+self.name+' is reading...')

class Student(Person):
    def read(self):
        print('The student is '+self.name+' is reading...')

class Teacher(Person):
    def read(self):
        print('The teacher is '+self.name+' is reading...')

def read_twice(person):
    person.read()
    person.read()
>>>read_twice(Person('xiaoming'))
>>>The person is xiaoming is reading...
>>>read_twice(Student('xiaoli'))
>>>The student is xiaoli is reading...
>>>read_twice(Teacher('xiaoliu'))
>>>The teacher is xiaoliu is reading...

新增Person的子类,不必对read_twice( ) 进行任何修改,就可以正常运行。

  • 实际上,任何依赖Person作为参数的方法和函数
    都可以不加修改正常运行,这就是多态
  • 由于Person类型有read( )方法,因此,传入的任意
    类型,只要是Person类或者子类,就会自动调用
    实际类型的read( )方法。

只管调用,不管细节

鸭子类型

静态语言(例如Java),如果需要传入参数是
Person类型,则传入的对象必须是Person类型或者它的子类,否则,将无法调用read( )方法

  • Python作为动态语言,则不一定需要传入Person类 型,只需保证传入的对象有一个read( )方法就可以
class Timer:
    def read(self):
        print("Reading is good!")

read_twice(Timer())
>>>Reading is good!
>>>Reading is good!

动态语言的“鸭子类型”:并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来
像鸭子”,那它就可以被看做是鸭子。

封装

将抽象得到的属性和功能相结合,形成一个有机整体,隐藏对象的属性和实现细节,仅对外提供
公共访问方式的接口

  • 封装:无需知道对象的构造
  • 多态:无需知道对象的类型,就能调用其方法

通过 类名. 或者 对象名. 形式访问封装内部的属性和方法

class Person:
    add='Beijing'
    def __init__(self,name):
        self.name=name
    def read(self):
        print('The person '+self.name+'is reading')

>>>p=Person('Hello')
>>>p.add
>>>'Beijing'
>>>p.read()
>>>The person Helli is reading

封装的优点

好处:

  • 保护隐私、提高安全性
  • 将变化隔离,便于使用
  • 提高复用性

模块

模块:Python中,可理解为对应于一个.py文件,将属性变量和实现方法封装在这个文件中

  • 任何Python 程序可作为模块导入,通过import 模块名导入(import jieba, import time…)
  • 一般用module_name.fun_name、module_name.var_name进行使用(模块名是文件名
    去掉.py后缀)

包(package): 将多个模块分为一个包

  • 包是python模块的有层次的文件目录结构
  • 包中包含模块和子包
  • 包中必须存在__init__.py文件,包的标识,一般不在
    init.py 中写入模块,尽可能保证简单

常见包结构

包中包含多个模块

包中包含多个模块和子包

若main.py要引用package_b中的test_fact模块,导入包
可以使用:

通过模块名.函数名进行引用

from package_b import test_fact
print(test_fact.fact(3))

需要通过完整的名称进行引用

import package_b.test_fact
print(package_b.test_fact.fact(3))

若main.py要引用子包package_a_a中的hello模块,导
入包可以使用:

from package import item 方式导入包时,这个子项(item)既可以是子包也可以是其他命名,如函数、类、变量等

  • 例如, from package_a.package_a_a.hello import say_hello
  • import item.subitem.subsubitem 这样的语法时,这些子项必须是包,最后的子项可以是包或模块,但不能是类、函数、变量等
  • 例如,import package_a.package_a_a.hello
  • 从* 导入包,利用import *找出包中的所有模块,然后导入
  • 在需要导入所有模块的包的__init__.py文件中,定义模糊导入:all=[‘模块1’,‘模块2’,…]
  • 慎用 import *
posted @ 2019-09-22 11:28  梦小冷  阅读(152)  评论(0编辑  收藏  举报