欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

python面向对象编程实例讲解

程序员文章站 2022-07-02 19:03:17
python面向对象编程全解。 面向对象技术简介 一个类占有一个独立的空间,类中的属性(函数外)叫做类变量,类中的函数,叫做类的方法。类中的方法第一个参数self,表示的是实例...

python面向对象编程全解。

面向对象技术简介

一个类占有一个独立的空间,类中的属性(函数外)叫做类变量,类中的函数,叫做类的方法。类中的方法第一个参数self,表示的是实例对象,不是类。

类(Class):用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个实例对象所共有的属性和方法。对象是类的实例(对象是类实例化之后的结果)。

类变量:定义在类中且在函数体之外。类变量通常不作为实例变量使用。实例对象无法修改类变量,不过可以读取类变量,只能添加同名属性覆盖类变量

数据成员:类变量和实例变量统称为数据成员。用于处理类及其实例对象的相关的数据。

方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。

实例变量:定义在方法中的变量,只庸作为当前实例的属性。

继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟”是一个(is-a)”关系(例图,Dog是一个Animal)。

实例化:浅复制类的空间到一个独立的空间。创建一个类的实例,类的具体对象。

方法:类中定义的函数。包含实例方法,类方法,静态方法。

对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。

python面向对象编程实例讲解

类中的类方法中self表示类本身,实例方法无法调用。

实例对象中的类方法中self表示类本身,实例方法中的self表示实例对象本身

实例化,复制类到一个独立的空间(会复制类中的公共类变量和实例方法、类方法、静态方法到实例对象的独立空间)。实例化的对象可以访问这个独立空间的所有属性和方法。在调用实例方法时将自身传递到函数中。函数的第一个参数self表示的是实例对象本身,self.name表示访问实例对象的属性,如果实例对象不存在就会访问实例空间的类变量,如果类变量也不存在或者不能访问(私有),则就会为实例对象创建这个属性。

创建类
#!/usr/bin/python
# -*- coding: UTF-8 -*-
class Employee:
    '所有员工的基类'
    empCount = 0

    def __init__(self, name, salary):
        self.name = name
        self.salary = salary
        Employee.empCount += 1

    def displayCount(self):
        print("TotalEmployee %d" % Employee.empCount)

    def displayEmployee(self):
        print("Name: ", self.name, ", Salary: ", self.salary)

empCount 变量是一个类变量,所有类的实例对象都具有这个成员。你可以在内部类或外部类使用 Employee.empCount 访问。

第一种方法__init__()方法是一种特殊的方法,被称为类的构造函数或初始化方法,当创建了这个类的实例时就会调用该方法。一个类可以有多个构造函数

self 代表类的实例,self 在定义类的方法时是必须有的,虽然在调用时不必传入相应的参数。Self代表了实例对象,而不是类,那就是:类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self。

创建实例对象

实例化类其他编程语言中一般用关键字 new,但是在 Python 中并没有这个关键字,类的实例化类似函数调用方式。

以下使用类的名称Employee 来实例化,并通过 __init__ 方法接受参数。

"创建 Employee 类的第一个对象"
emp1 = Employee("Zara", 2000)
"创建 Employee 类的第二个对象"
emp2 = Employee("Manni", 5000)
操作对象属性

下面是读取对象属性的实例:

#!/usr/bin/python
# -*- coding: UTF-8 -*-
class Employee:
    '所有员工的基类'
    empCount = 0
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary
        Employee.empCount += 1


    def displayCount(self):
        print("Total Employee %d" % Employee.empCount)


    def displayEmployee(self):
        print("Name : ", self.name, ", Salary: ", self.salary)
"创建 Employee 类的第一个对象"
emp1 =Employee("Zara", 2000)
"创建 Employee 类的第二个对象"
emp2 =Employee("Manni", 5000)
emp1.displayEmployee()
emp2.displayEmployee()
print "TotalEmployee %d" % Employee.empCount

以下函数还可以对属性进行读取之外的操作:

getattr(obj,name[, default]) : 访问对象的属性。
hasattr(obj,name): 检查是否存在一个属性。
setattr(obj,name,value): 设置一个属性。如果属性不存在,会创建一个新属性。
delattr(obj,name) : 删除属性。
Python内置的类属性
__dict__ : 类的属性(包含一个字典,由类的数据属性组成)
__doc__ :类的文档字符串
__name__: 类名
__module__: 类定义所在的模块(类的全名是'__main__.className',如果类位于一个导入模块mymod中,那么className.__module__ 等于 mymod)
__bases__ : 类的所有父类构成元素(包含了一个由所有父类组成的元组)
Python内置的类方法
__init__ 构造函数,在生成对象时调用
__del__ 析构函数,释放对象时使用
__repr__ 打印,转换
__setitem__按照索引赋值
__getitem__按照索引获取值
__len__获得长度
__cmp__比较运算
__call__函数调用
__add__加运算
__sub__减运算
__mul__乘运算
__p__除运算
__mod__求余运算
__pow__称方




我们可以为这些函数进行重写,实现我们想要的功能。例如这里对所有的读写操作进行监督屏蔽。
def __getitem__(self,key):                 #在字典中获取值时会自动调用 __getitem__函数,设置值时会自动调用__setitem__函数
    print("-------------派生类字典读取值")
def __setitem__(self,key,value):
    print("-------------派生类字典设置值")
def __getattr__(self,name):               #读取类属性(包括继承的属性)会自动执行__getattr__函数,设置属性会自动执行__setattr__函数
    print("-------------读取派生类属性")
def __setattr__(self,name,value):
    print("-------------设置派生类属性值")


类的继承

面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制。继承完全可以理解成类之间的类型和子类型关系。

派生类继承的是基类的类变量(不包含实例变量)和方法。在派生类中调用基类的构造函数时,传入的self是派生类的实例对象的引用。所以此时基类的构造函数新增加的实例变量,直接添加到了派生类实例对象上。所以整个过程中,我们把构造函数想想成初始化函数更好。并且在继承过程中,不存在基类的实例对象。

在python中继承中的一些特点:

1:在继承中基类的构造(__init__()方法)不会被自动调用,它需要在其派生类的构造中亲自专门调用。

2:在调用基类的方法时,需要加上基类的类名前缀,且需要带上self参数变量。区别于在类中调用普通函数时并不需要带上self参数

3:Python总是首先查找对应类型的方法,如果它不能在派生类中找到对应的方法,它才开始到基类中逐个查找。(先在本类中查找调用的方法,找不到才去基类中找)。

class Parent:       # 定义父类
    parentAttr = 100
    def __init__(self):
        print("调用父类构造函数")
    def parentMethod(self):
        print('调用父类方法')
    def setAttr(self, attr):
        Parent.parentAttr = attr
    def getAttr(self):
        print("父类属性 :", Parent.parentAttr)

class Child(Parent):
    # 定义子类
    def __init__(self):
        print("调用子类构造方法")  # 无论子类还是父类,都要单独写一次_init_
    def childMethod(self):
        print('调用子类方法')
    def getAttr(self):
        print('重写父类方法,因为父类方法不能满足需求')



c = Child()  # 实例化子类
c.childMethod()  # 调用子类的方法
c.parentMethod()  # 调用父类方法
c.setAttr(200)  # 再次调用父类的方法 - 设置属性值
c.getAttr()  # 再次调用父类的方法 - 获取属性值

你可以使用issubclass()或者isinstance()方法来检测,一个类或对象是否为其他类或对象的子类。

issubclass() - 布尔函数判断一个类是另一个类的子类或者子孙类,语法:issubclass(sub,sup)

isinstance(obj, Class) 布尔函数如果obj是Class类的实例对象或者是一个Class子类的实例对象则返回true。

如果在继承元组中列了一个以上的类,那么它就被称作”多重继承” 。

语法:

派生类的声明,与他们的父类类似,继承的基类列表跟在类名之后。

多态

如果父类方法的功能不能满足需求,可以在子类重写父类的方法。实例对象调用方法时会调用其对应子类的重写后的方法

运算符重载

Python同样支持运算符重载,实例如下:

class Vector:
    def __init__(self, a, b):
        self.a = a
        self.b = b
    def __str__(self):
        return 'Vector(%d, %d)' % (self.a, self.b)
    def __add__(self, other):
        return Vector(self.a + other.a, self.b + other.b)

v1 = Vector(2, 10)
v2 = Vector(5, -2)
print(v1 + v2)

以上代码执行结果如下所示:

Vector(7,8)
类的私有属性及方法

1)类的私有属性

__private_attrs:两个下划线开头,声明该属性为私有,不能在类的外部被使用或直接访问。在类内部的方法中使用时self.__private_attrs。

2)类的私有方法

__private_method:两个下划线开头,声明该方法为私有方法,不能在类地外部调用。在类的内部调用self.__private_methods

3)实例


class JustCounter:
    __secretCount = 0    # 私有变量
    publicCount = 0      # 公开变量
    def count(self):
        self.__secretCount += 1
        self.publicCount += 1
        print(self.__secretCount)  # 在内部使用私有化属性,不会产生错误

counter = JustCounter()
counter.count()
counter.count()
print(counter.publicCount)
print(counter.__secretCount) # 报错,实例不能访问私有变量
单下划线、双下划线、头尾双下划线说明

实例对象无法访问私有的类变量,但是可以添加同名的私有实例变量。

__foo__: 定义的是特列方法,类似 __init__() 之类的。

_foo: 以单下划线开头的表示的是 protected 类型的变量,即保护类型只能允许其本身与子类进行访问,不能用于 from module import *

__foo: 双下划线的表示的是私有类型(private)的变量, 只能是允许这个类本身进行访问了。

方法

实例方法:只能通过实例调用,实例方法第一个定义的参数只能是实例本身引用。也就是说在实例方法中修改实例变量。可以读取实例变量和类变量。

class Myclass:
  def foo(self):
    print(id(self),'foo')

a=Myclass()#既然是实例对象,那就要创建实例
a.foo()#输出类里的函数地址
print(id(a))#输出类对象的地址

#结果地址一样

类方法:定义类方法,要使用装饰器@classmethod,定义的第一个参数是能是类对象的引用,可以通过类或者实例直用。类方法用来修改类变量。

class Myclass:
    @classmethod#类装饰器
    def foo2(cls):
        print(id(cls),'foo2')  

#类对象,直接可以调用,不需要实例化
print(id(Myclass),'yy')
Myclass.foo2()#直接可以调用

静态方法:定义静态方法使用装饰器@staticmethod,没有默认的必须参数,通过类和实例直接调用

class Myclass:
  @staticmethod#静态方法
  def foo3():
    print('foo3')

Myclass.foo3()#没有参数
a.foo3()
#结果foo3
全解demo

People.py模块

__all__=["default_age","default","Moniter"]            # __all__变量,设置导入模块import*时将会自动引入的函数、变量、类
default_age = 12                                          #定义模块变量
def set_default_age(age=13):                               #定义模块函数
    print("默认年龄为"+str(age)+"岁")


class Parent(object):                                  #定义模块类。()内指定基类,当是object可以省略不写
    print("定义了Student类")                          #定义类时就会执行内部的执行语句
    default_name='student'                             #定义一个类变量
    __default_age=12                                    #函数名或属性名前面为双下划线表示成员私有。只能在当前类或对象空间内使用。
    def __init__(self, name1='student1',age1=13):     #init是构造函数,self代表类的实例对象。参数可以设置默认值。
        self.name=name1                                 #自动新增两个实例变量
        self.age=age1
        print("基类构造函数设置了"+self.name)
    def getname(self):                                  #实例方法,函数名为引用变量,可以进行赋值,即变更函数体。函数引用变量调用时使用(),不带括号代表变量。getname代表函数引用变量,getname()代表代表调用函数
        print('基类读取名称'+self.name)
        return self.name
    def setname(self,name1):
        self.name=name1
        print("基类设置名称"+self.name)


# 派生类继承了基类的类变量和类方法和实例方法(没有实例变量,因为实例变量是在实例以后才存在的)
class Child(Parent):                            #生成派生类,可以多重继承,但是应尽量避免。继承后会包含基类的函数和特性。
    def __init__(self,name1="child"):          #派生类构造函数不会自动调用基类构造函数
        self.name = name1                       #在当前实例对象中新增name实例变量
        Parent.__init__(self)                   #两种方法,调用超类的构造函数。基类中就修改了name实例变量,新增了age实例变量
        # super(Child,self).__init__(name1)
        print("派生类构造函数"+self.name)                #这里读取的就是最后一次对name的修改(基类中对他的修改)

    def setname(self, name1):                           #重写基类中的方法。其实是在派生类中添加了一个新方法,因此在查找此函数时就必用向上查找了。
        self.name = "新"+name1
        print("基类设置名称" + self.name)

    def getage(self):                                   #派生类添加新的实例方法
        print("派生类读取年龄"+str(self.__default_age)) #派生类是无法读取基类的私有类变量的。因此这句话会报错

    @staticmethod                                       #@staticmethod声明函数为静态方法,通过类和实例直接调用
    def info():
        print("派生类的静态函数")

    @classmethod                                        #@classmethod声明函数为类方法,第一个参数是能是类对象的引用,可以通过类或者实例直用
    def setsex(self):                                  #类方法self表示对类的引用
        self.sex='男'                                   #添加类变量
        print("派生类设置性别"+self.sex)


print('Peopeo类开始运行')    #导入模块或执行模块都会执行函数

# 当一个module被执行时,moduel.__name__的值将是"__main__",而当一个 module被其它module引用时,module.__name__将是module自己的名字
if __name__=="__main__":     #只有在执行当前模块时才会运行此函数
    set_default_age()


调用函数

# 只有有__init__.py文件的文件夹才能是package,才支持引入
import People    #引用此模块,就相当于将此模块在此处展开。

# #调用模块变量、模块函数
# People.set_default_age(People.default_age)
# print('================================')
# #调用模块中的类
parent1 = People.Parent('student1')   #调用类的构造函数,实例化一个对象,不需要new,会添加两个实例变量name和age
print(parent1.default_name)             #读取实例的数据成员(类变量和实例变量)
parent1.default_name='luanpeng'        #修改实例的数据成员
People.Parent.default_name='Student'   #修改类变量
parent1.name='luanpeng'               #访问修改实例对象中的属性
parent2 = People.Parent('student2')   #调用类的构造函数,实例化一个对象,不需要new
print(parent2.default_name)            #读取实例的数据成员(类变量和实例变量)
# # print(parent1.__default_age)       #实例变量没有复制类的私有类变量
parent1.__default_age=14              #添加一个私有的实例变量
print(parent1.__default_age)          #可以访问自身的私有实例变量
parent1.getname()                     #调用类方法

print('================================')
child1 = People.Child()  #调用类的构造函数,实例化一个对象
print('================================')
child1.setname('child')  #调用派生类重写或继承的方法
# child1.getage()  #调用派生类添加的方法,方法类访问了基类的私有变量,会报错
print('================================')
child1.info()         #调用静态函数,方法1
People.Child.info()   #调用静态函数,方法2
print('================================')
# print(child1.sex)   #类变量中不存在sex,所以无法查找到,访问出错
child1.setsex()    #调用类方法,添加类变量
print(child1.sex)  #访问类变量
child2 = People.Child()  #使用修改后的类重新实例化一个对象
print(child2.sex)     #可以查找到类变量sex

print('================================')
print(issubclass(People.Child,People.Parent)) # 布尔函数(Child,Parent)判断一个类是另一个类的子类或者子孙类,语法:issubclass(sub,sup)
print(isinstance(child1, People.Parent))  #布尔函数如果obj是Class类的实例对象或者是一个Class子类的实例对象则返回true。
print('================================')