我是靠谱客的博主 生动魔镜,最近开发中收集的这篇文章主要介绍Python面向对象,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

       面向对象编程(Object Oriented Programming,OOP),是一种程序设计思想,把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。面向过程的程序设计把计算机程序视为一系列命令集合,即一组函数的顺序执行。为了简化程序设计,面向过程把函数继续切分为子函数,即把大块函数通过切割成小块函数来降低系统的复杂度。而面向对象的程序设计把计算机程序视为一组对象集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。

       那面向对象编程与面向过程编程到底有什么区别?通过一个例子进行比较:

#面向过程
stu1 = {"name":"aaa","score":90}
stu2 = {"name":"bbb","score":85}
def print_score(stu):
    print("%s: %s" %(stu["name"],stu["score"]))

print_score(stu1) #aaa: 90
print_score(stu2) #bbb: 85

------------这是分界线-----------------

#面向对象,面向对象的设计思想是抽象出class,根据class创建instance
class Student(object):
    #实例属性
    def __init__(self,name,score):
        self.name = name
        self.score = score
    #实例方法
    def print_score(self):
        print("%s: %s" %(self.name,self.score))

stu1 = Student("aaa",90) #创建实例stu1
stu2 = Student("bbb",85) #创建实例stu2
stu1.print_score() #调用方法,输出aaa: 90
stu2.print_score() #调用方法,输出bbb: 85

1.类和实例

       面向对象最重要的概念就是类(class)实例(instance)抽象的模板,如上述Student类,而实例是根据类创建出来的一个个具体“对象”,如上述stu1对象。

#Python类定义格式
class Classname(object):
    psss

#说明:class关键字定义类,后面紧接类名(类名通常首字母大写),紧接着为(object),表该类是从哪个类继承下来的。如果没有合适的继承类,就用object类,这是所有类最终都会继承的类。
#Python实例
instancename = Classname() #创建了实例instancename,创建实例通过类名+()实现

       __init__

class Student(object):
    #实例属性
    #__init__()叫做初始化方法(构造方法),在类被调用时,这个方法会自动执行,进行一些初始化动作。
    def __init__(self,name,score):
        self.name = name
        self.score = score
    #实例方法
    def print_score(self):
        print("%s: %s" %(self.name,self.score))

    #析构方法,在实例释放、销毁的时候自动执行,通常用于做一些收尾工作。这个方法可选,不是必须写
    def __del__(self):
        print("这里是析构函数,结束啦!")

stu1 = Student("张三",90)#生成一个角色,会自动把参数传给Student下面的__init__()方法,此时相当于Student(stu1,"张三",90)
stu1.print_score()#此时相当于Student.print_score(stu1)

       注意到__init__方法的第一个参数永远是self表示创建的实例本身,因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。有了__init__方法,在创建实例的时候,就不能传入空的参数了,必须传入与__init__方法匹配的参数,但self不需要传,Python解释器自己会把实例变量传进去:

stu1 = Student("aaa",90) #实例stu1
print(stu1.name) #aaa
print(stu1.score) #90

       和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self,并且,调用时,不用传递该参数。除此之外,类的方法和普通函数没有什么区别!

2.访问限制

class Student(object):
    def __init__(self,name,score):
        self.name = name
        self.score = score

    def print_score(self):
        print('%s: %s' %(self.name,self.score))

s1 = Student('zhangsan',90)
print(s1.name)#打印实例名称,这里输出为zhangsan
print(s1.score)#打印实例成绩,这里输出为90

s1.score = 100 #在类外部,我们修改实例属性
print(s1.score)#打印输出为修改后的100

       通过上述代码可以发现,类虽然隐藏了内部的复杂逻辑,但是其内部属性可以被外部代码任意修改其属性,有没有什么方法可以不让外部代码修改呢?在Python中,实例变量名如果以 __ 开头,就变成了一个私有变量,只有内部可以访问,外部不能访问,所以,上述代码可以这么修改:

class Student(object):
    def __init__(self,name,score):
        self.__name = name #原self.name修改为self.__name
        self.__score = score #原self.score修改为self.__score

    def print_score(self):
        print('%s: %s' %(self.__name,self.__score)) #修改
s1 = Student('zhangsan',90)
print(s1.__name)#报错,AttributeError: 'Student' object has no attribute '__name'
print(s1.__score)#报错,AttributeError: 'Student' object has no attribute '__score'
#说明:这里已经无法从外部访问 实例变量.__name和实例变量.__score 

       上述修改确保了外部代码不能随意修改对象内部状态,但是改的太过于彻底了,我们都无法访问其属性了~~~~。如果我们还想获取属性name与score应该怎么办?可以给Student类增加get_name和get_score方法。

class Student(object):
    def __init__(self,name,score):
        self.__name = name
        self.__score = score

    def print_score(self):
        print('%s: %s' %(self.__name,self.__score))

    def get_name(self):
        return self.__name #获取name属性

    def get_score(self):
        return self.__score #获取score属性

    def set_name(self,name):
        self.__name = name #修改name属性
        return self.__name

    def set_score(self,score):
        self.__score = score #修改score属性

s1 = Student('zhangsan',90)
print(s1.get_name())#zhangsan
print(s1.get_score())#90
print(s1.set_name('lisi')) #lisi

       说了半天,这样一番周折(看起来有点化简为繁,代码量增加)有什么优势?其实这样相比于直接s1.score = 100,在set_score方法中,可以对参数做检查,更符合实际要求。

class Student(object):
    ...

    def set_score(self,score):
        if 0<=score<=100:
            self.__score = score
        else:
            raise ValueError('成绩不规范')

在Python中,变量名类似 __xxx__ ,即双下划线开头,并且双下划线结尾的,是特殊变量,可以直接访问;

                      变量名类似_xxx,即一个下划线开头,这种实例变量外部是可以访问的,按照约定成俗的规定,这样的变量意思是“虽然可以被访问,但是,请将其视为私有变量,不要随意访问”

                       变量名类似__xxx,即双下划线开头,这种变量不可以被外部访问,需要在类内部写相应方法。是不是外部绝对不能访问呢?实际上也不是,实际上通过 _类名__xxx仍可以访问到 __xxx变量,但是强烈不建议这么干!!!

3.封装、继承和多态

       (1)继承

#继承
class Person(object):
    def speak(self):
        print('People is speaking...')

class Teacher(Person):
    pass

class Student(Person):
    pass

teacher1 = Teacher()
teacher1.speak() #People is speaking...
student1 = Student()
student1.speak() #People is speaking...
#注:Person为父类,Teacher、Student为其子类,子类自动继承父类方法
#继承--新市类super用法
class Persion(object):
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def speak(self):
        print("People is speaking...")

class Teacher(Persion):
    def __init__(self,name,age,course,salary):
        #Persion.__init__(self,name,age) #父类实现,另一种方法见super,这种写法多继承不太方便
        super(Teacher, self).__init__(name,age)
        self.course = course
        self.salary = salary

    def teaching(self):
        print("Teacher %s is teaching %s"%(self.name,self.course))

class Student(Persion):
    def __init__(self,name,age,grade,sid):
        #Persion.__init__(self,name,age) #父类实现,另一种方法见super,这种写法多继承不太方便
        super(Student, self).__init__(name,age)
        self.grade = grade
        self.sid = sid

    def study(self):
        print("The student %s ID is %s"%(self.name,self.sid))
        
teacher1 = Teacher("Jack",30,"C++",3000)
teacher1.teaching() #Teacher Jack is teaching C++
student1 = Student("Tom",20,"大一","201911")
student1.study() #The student Tom ID is 201911

       (2)多态 

#多态
class Person(object):
    def speak(self):
        print('People is speaking...')

class Teacher(Person):
    def speak(self):
        print('Teacher is speaking...')
    def work(self):
        print('Teacher is working...')

class Student(Person):
    def speak(self):
        print('Student is speaking...')
    def learn(self):
        print('Student is learning...')

teacher1 = Teacher()
teacher1.speak() #Teacher is speaking...
student1 = Student()
student1.speak() #Student is speaking...
#注:当子类和父类都存在相同的speak()方法时,子类的speak()覆盖了父类的speak(),在代码运行的时候总是调用子类的speak()
#开闭原则
class Person(object):
    def speak(self):
        print('People is speaking...')

class Teacher(Person):
    def speak(self):
        print('Teacher is speaking...')
    def work(self):
        print('Teacher is working...')

class Student(Person):
    def speak(self):
        print('Student is speaking...')
    def learn(self):
        print('Student is learning...')

#新增一个Person子类,不必对run_twice()做任何修改,任何依赖Person作为参数的函数或者方法都可以不加修改正常运行,原因就在于多态
class Test(Person):
    def speak(self):
        print('Testing...')

def run_twice(people):
    people.speak()
    people.speak()

run_twice(Person()) #People is speaking...   People is speaking...
run_twice(Teacher()) #Teacher is speaking... Teacher is speaking...
run_twice(Student()) #Student is speaking... Student is speaking...
run_twice(Test()) #Testing...   Testing...

       多态的好处就是,当我们需要传入 Teacher、Student、Test...时,我们只需要接收Person类型就可以了,因为 Teacher、Student、Test...都是Person类型,然后按照Person类型进行操作即可。由于Person类型有speak()方法,因此,传入任意类型,只要是Person类或者子类就会自动调用实际类型的speak()方法,这就是多态的意思:

       对于一个变量,我们只需要知道它是Person类型,无需确切地知道它的子类型,就可以放心地调用speak()方法,而具体调用的speak()方法是作用在Person、Teacher、Student、还是Test对象上,由运行该对象的确切类型决定,这就是多态的真正威力:调用方只管调用,不管细节,而我们新增一种Person的子类时,只需确保speak()方法编写正确,不用管原来代码是如何调用的,这就是著名的“开闭”原则:

       对扩展开放:允许新增Person子类

       对修改封闭:不需要修改依赖Person类型的run_twice()等函数

       静态语言VS动态语言

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

       动态语言(如Python):不一定需要传入Person类型,我们只需要保证传入的对象有一个speak()方法就可以了。

#鸭子类型:并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做鸭子
class Person(object):
    def speak(self):
        print('People is speaking...')

class Teacher(Person):
    def speak(self):
        print('Teacher is speaking...')
    def work(self):
        print('Teacher is working...')

class Student(Person):
    def speak(self):
        print('Student is speaking...')
    def learn(self):
        print('Student is learning...')

#新增一个Person子类,不必对run_twice()做任何修改,任何依赖Person作为参数的函数或者方法都可以不加修改正常运行,原因就在于多态
class Test(Person):
    def speak(self):
        print('Testing...')

#鸭子类型,不需要判断参数是Person类型,只要对象有speak()方法,就能调用成功!
class Timer(object):
    def speak(self):
        print("鸭子类型...")

def run_twice(people):
    people.speak()
    people.speak()

run_twice(Person()) #People is speaking...   People is speaking...
run_twice(Teacher()) #Teacher is speaking... Teacher is speaking...
run_twice(Student()) #Student is speaking... Student is speaking...
run_twice(Test()) #Testing...   Testing...
run_twice(Timer()) #鸭子类型... 鸭子类型...

       总结:a、继承可以把父类的所有功能直接拿过来,这样不必重零做起,子类只需新增自己特有方法,也可以把父类不适合的方法覆盖重写。

                   b、动态语言的鸭子类型特点决定了继承不像静态语言那样是必须的。

4.type()和isinstance()

       type()主要用于获取未知变量的类型

       isinstance()只要用于判断子类是否继承父类

       isinstance()可以判断子类对象是否继承与父类,而type()不可以

     dir()

       获取一个对象的所有属性和方法,可以使用dir()函数,它返回一个包含字符串的list,比如,获得一个str对象的所有属性和方法:

#dir()函数
>>> dir('ABC')
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
>>>
#getattr、setattr、hasattr
>>> class MyObject(object):
...     def __init__(self):
...             self.x = 9
...     def power(self):
...             return self.x * self.x
...
>>> obj = MyObject()
>>> hasattr(obj,'x') #有属性"x"吗?
True
>>> hasattr(obj,'y') #有属性"y"吗?
False
>>> setattr(obj,'y',2) #设置一个属性"y"
>>> hasattr(obj,'y')
True
>>> getattr(obj,'y') #获取属性"y"
2
>>> getattr(obj,'x') #获取属性"x"
9
>>> getattr(obj,'z') #获取属性"z",属性不存在抛出AttributeError
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'MyObject' object has no attribute 'z'

5.实例属性和类属性

#实例属性和类属性
class Person(object):
    city = 'XIAN' #类属性,可以直接在类中定义,归Person类所有
    def __init__(self,name):
        self.name = name

person1 = Person('Tom') #创建实例
person1.age = 24 #给实例绑定age属性
person1.city
print(person1.name) #Tom
print(person1.age) #24
print(person1.city) #XIAN
person1.city = 'Shanghai' #给实例绑定city属性,由于实例属性优先级比类属性高,因此会屏蔽掉类的city属性
print(person1.city) #Shanghai
del person1.city #删除实例city属性
print(person1.city) #XIAN,再次调用person1.city,由于实例city属性没有找到,类的city属性显示
 #注:在编写程序时,千万不要对实例属性和类属性使用相同的名字,因为相同的名称实例属性将屏蔽类属性,但是当删除实例属性后,再使用相同的名称,访问到的是类属性
#练习:统计学生人数——每创建一个实例,该属性自动增加
class Student(object):
    count = 0 #实例属性,属于所有类所有,所有实例共享一个属性
    def __init__(self,name):
        self.name = name
        Student.count += 1

s1 = Student('AAA')
s2 = Student('BBB')
s3 = Student('CCC')
print(Student.count) #3

6.经典类vs新式类

#多继承新式类、经典类区别
#经典类:深度优先  新式类:广度优先
class A:
    def __init__(self):
        self.n = 'A'

class B(A):
    # def __init__(self):
    #     self.n = 'B'
    pass

class C(A):
    # def __init__(self):
    #     self.n = 'C'
    pass

class D(B, C):
    # def __init__(self):
    #     self.n = 'D'
    pass

obj = D()
print(obj.n)

 7.静态方法、类方法、属性方法

       (1)静态方法

       通过@staticmethod装饰器即可以把其装饰的方法变为一个静态方法,什么是静态方法呢?其实不难理解,普通的方法,可以在实例化后直接调用,并且在方法里可以通过self. 调用实例变量或类变量,但静态方法是不可以访问实例变量或类变量的,一个不能访问实例变量和类变量的方法,其实相当于跟类本身已经没什么关系了,它与类唯一的关联就是需要通过类名来调用这个方法。

#静态方法
# -*- coding:utf-8 -*-
class Dog(object):
    def __init__(self,name):
        self.name = name

    @staticmethod #把eat方法变为静态方法
    def eat(self):
        print("%s is eating"%self.name)

d = Dog("大黄")
#d.eat() #这样调用会报错,当eat变为静态方法以后,再通过实例调用时就不会自动把实例本身当作一个参数传给self了
#方法一:调用时主动传递实例本身给eat方法,即d.eat(d)
class Dog(object):
    def __init__(self,name):
        self.name = name

    @staticmethod #把eat方法变为静态方法
    def eat(self):
        print("%s is eating"%self.name)

d = Dog("大黄")
d.eat(d)
#方法二:在eat方法中去掉self参数,但这也意味着在eat中不能通过self.调用实例中的其他变量了
class Dog(object):
    def __init__(self,name):
        self.name = name

    @staticmethod #把eat方法变为静态方法
    def eat():
        print("%s is eating"%("二黄"))

d = Dog("大黄")
d.eat()

       (2)类方法

       类方法通过@classmethod装饰器实现,类方法和普通方法的区别是,类方法只能访问类变量,不能访问实例变量。

#类方法
class Dog(object):
    def __init__(self,name):
        self.name = name

    @classmethod
    def eat(self):
        print("%s is eating"%self.name)

d = Dog("大黄")
d.eat() #报错,因为name是个实例变量,类方法不能访问实例变量
#可以定义一个类变量,叫name1
class Dog(object):
    name1 = "这里是类变量"
    def __init__(self,name):
        self.name = name

    @classmethod
    def eat(self):
        print("%s is eating"%self.name1)

d = Dog("大黄")
d.eat() #这里是类变量 is eating

       (3)属性方法

       属性方法的作用就是通过@property把一个方法变为一个静态属性

#属性方法
class Dog(object):
    def __init__(self,name):
        self.name = name

    @property
    def eat(self):
        print("%s is eating"%self.name)

d = Dog("大黄")
d.eat() #报错,因为eat此时已经变成一个静态属性了,不是方法了,想调用已经不需要加()了,直接d.eat就可以了

#直接d.eat
class Dog(object):
    def __init__(self,name):
        self.name = name

    @property
    def eat(self):
        print("%s is eating"%self.name)

d = Dog("大黄")
d.eat

       把一个方法变成静态属性有什么用呢?既然想要静态变量,那直接定义成一个静态变量不就得啦?以后你会遇到很多场景是不能 简单通过定义静态属性来实现的,比如,你想知道一个航班当前的状态,是到达了、延迟了、取消了、还是已经飞走了,想知道这种状态必须经历以下几步:

       ①连接航空公司API查询

       ②对查询结果进行解析

       ③返回结果给你的用户

       因此这个status属性的值是一系列动作后才得到的结果,所以每次调用时,其实它都要经过一系列的动作才返回结果,但这些动作过程不需要用户关心,用户只需要调用这个属性就可以。

class Flight(object):
    def __init__(self,name):
        self.flight_name = name


    def checking_status(self):
        print("checking flight %s status " % self.flight_name)
        return  1

    @property
    def flight_status(self):
        status = self.checking_status()
        if status == 0 :
            print("flight got canceled...")
        elif status == 1 :
            print("flight is arrived...")
        elif status == 2:
            print("flight has departured already...")
        else:
            print("cannot confirm the flight status...,please check later")

f = Flight("东方航空")
f.flight_status

       现在我只能查询航班状态, 既然这个flight_status已经是个属性了, 那我能否给它赋值呢?试试吧

f = Flight("东方航空")
f.flight_status
f.flight_status =  2

       输出, 说不能更改这个属性,怎么办怎么办???当然可以改, 不过需要通过@proerty.setter装饰器再装饰一下,此时你需要写一个新方法, 对这个flight_status进行更改。当然@flight_status.deleter允许可以将这个属性删除 。

# -*- coding:utf-8 -*-
class Flight(object):
    def __init__(self,name):
        self.flight_name = name

    def checking_status(self):
        print("checking flight %s status"%self.flight_name)
        return 1

    @property
    def flight_status(self):
        status = self.checking_status()
        if status == 0:
            print("flight got canceled...")
        elif status == 1:
            print("flight is arrived...")
        elif status == 2:
            print("flight has departured already...")
        else:
            print("cannot confirm the flight status...,please check later")

    @flight_status.setter
    def flight_status(self,status):
        status_dic = {
            0:"canceled",
            1:"arrived",
            2:"departured"
        }
        print("has changed the flight status to ",status_dic.get(status))

    @flight_status.deleter
    def flight_status(self):
        print("status got removed...")

f = Flight("东方航空")
f.flight_status
f.flight_status = 2
del f.flight_status

 

最后

以上就是生动魔镜为你收集整理的Python面向对象的全部内容,希望文章能够帮你解决Python面向对象所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(54)

评论列表共有 0 条评论

立即
投稿
返回
顶部