Python菜鸟编程第十课之面向对象

Python菜鸟编程第十课之面向对象

1.面向过程与面向对象编程

  • 面向过程:

    所谓过程,就是我们解决问题的步骤,一步步的按照流程走,有先后之分。整个设计就好比流水线,思维上比较机械。

    优点:复杂问题流程化,将问题分解。

    缺点:拓展性不好

  • 面向对象:

    对象是一个数据以及相关行为的集合,面向对象是功能上指向建模对象。面向对象是通过数据和行为方式来描述交互对象的集合。

    优点:解决程序的拓展性

    缺点:复杂度远高于面向过程。交互式解决问题,无法准确预测结果。

object1
Tom
	特征:
    school=zucc
    name=tom
    age=20
    sex=male
    
    技能:
    eat
    study
    sleep
   
object2
Jack
	特征:
    school=zucc
    name=jack
    age=21
    sex=male
    
    技能:
    eat
    study
    sleep
    sing

类就是类别、种类。对象就是特征和技能的统一体。类则是这一系列对象的特征和技能的结合。现实生活中,先有个体(对象)才有类别,但对于程序,必须先有类,才会有对象。

2.面向对象编程

  • 类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
  • **方法:**类中定义的函数。
  • **类变量:**类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
  • **数据成员:**类变量或者实例变量用于处理类及其实例对象的相关的数据。
  • **方法重写:**如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
  • **局部变量:**定义在方法中的变量,只作用于当前实例的类。
  • **实例变量:**在类的声明中,属性是用变量来表示的。这种变量就称为实例变量,是在类声明的内部但是在类的其他成员方法之外声明的。
  • **继承:**即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟"是一个(is-a)"关系(例图,Dog是一个Animal)。
  • **实例化:**创建一个类的实例,类的具体对象。
  • **对象:**通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
OOP(object oriented programming)

OOP把对象作为程序的一个基本单元,一个对象就包含了数据和操作数据的函数。

在Python中,所有数据类型,都可以称之为对象,同时我们也可以自定义对象。

自定义的对象的数据类型就是面向对象中类(class)的概念。

demo1:

用面向对象编程来处理学生成绩。

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

    def find_score(self):
        print(self.name, ':', self.score)


stu1 = Student('Tom', 90)
stu1.find_score()
运行结果:
Tom : 90

用面向过程来处理学生成绩:

stu1 = {'name': 'Tom', 'score': 90}


def find_score(stu):
    print(stu['name'], ":", stu['score'])


find_score(stu1)
运行结果:
Tom : 90

demo2:

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

    def find_score(self):
        print(self.name, ':', self.score)

    def eating(self):
        print(self.name, '来自', self.school, ',今年', self.age, '岁,', '正在eat')

    def studying(self):
        print(self.name, '来自', self.school, ',今年', self.age, '岁,', '正在 study')


stu1 = Student('Tom', 90, 'zucc', 18)
stu1.find_score()
stu1.eating()
stu1.studying()
运行结果:
Tom : 90
Tom 来自 zucc ,今年 18, 正在eat
Tom 来自 zucc ,今年 18, 正在 study

3.类的定义和使用

3.1类的定义

类的命名,约定俗成使用大驼峰式命名。大驼峰就是变量名称的首字母大写。

语法:

class ClassName(object):
    '''说明文档'''
    def \__init__(self,para1,para2...):
        self.对象属性1=para1
        
    def 方法名1(self):
            pass
        
    class_statement
    
    
obj=ClassName(para1,para2)
#类的实例化,代表具体的东西,ClassName():调用\__init__,括号内传参,无需传入self,参数一一对应,结果返回对象obj

obj.对象属性1#查看对象的属性
obj.方法名#调用类的方法

类的两个作用:

  • 属性引用:类名.属性
  • 实例化:类名加括号就是实例化,它能够自动触发__init__函数,进而为每个实例定制自己的特征。

demo:1

定义一个简单的类。

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

stu1 = Student('Tom', 90, 'zucc', 18)#将类实例化
print(stu1.name,stu1.score)
运行结果:
Tom 90

和普通函数相比,在类中定义方法时,第一个参数必须是self。除了第一个参数外,其他的和普通函数没什么区别。

__init__方法:为对象初始化自己独有的特征。该方法中可以有任意的代码,但是一定不能有返回值。

demo2:

class Student:
    def test_instance(self):
        print(self)
        
stu1 = Student()
stu1.test_instance()
运行结果:
<__main__.Student object at 0x0000025338BB82B0>

self 代表的是实例,而非类。

3.2数据封装

demo:

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

    def find_score(self):
        print(self.name, ':', self.score)


stu1 = Student('Tom', 90)
stu1.find_score()

通过_init_()让stu1实例本身就拥有了相关数据,如果要访问这些数据,我们可以直接在student类的内部定义相关的函数来访问,以此来“封装”数据。这些封装数据的函数与Student类本身是关联起来的,称之为方法。

demo1:

class Point:
    pass


p1 = Point()
p1.x = 5
p1.y = 4
print(p1.x, p1.y)
运行结果:
5 4

通过点记法给实例化的对象赋予任何属性。

demo2:

class Point:
    def resect(self):
        self.x=0
        self.y=0


p1 = Point()
p1.resect()
print(p1.x, p1.y)
p1.x = 5
p1.y = 4
print(p1.x, p1.y)
运行结果:
0 0
5 4

demo:

import math


class Point:
    def move(self, x, y):
        self.x = x
        self.y = y

    def resect(self):
        self.move(0,0)

    def cal_distence(self, other_point):
        return math.sqrt((self.x - other_point.x) ** 2 + (self.y - other_point.y) ** 2)


p1 = Point()
p1.resect()
p2=Point()
p2.move(3,4)
print(p1.cal_distence(p2))
运行结果:
5.0

demo:

import math


class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def move(self, x, y):
        self.x = x
        self.y = y

    def resect(self):
        self.move(0, 0)

    def cal_distence(self, other_point):
        return math.sqrt((self.x - other_point.x) ** 2 + (self.y - other_point.y) ** 2)
p3 = Point(6, 8)
print(p3.x, p3.y)
运行结果:
6 8

3.3类属性的查看

  1. dir()

demo:

import math


class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def move(self, x, y):
        self.x = x
        self.y = y

    def resect(self):
        self.move(0, 0)

    def cal_distence(self, other_point):
        return math.sqrt((self.x - other_point.x) ** 2 + (self.y - other_point.y) ** 2)

print(dir(Point))
运行结果:
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'cal_distence', 'move', 'resect']

2.类名.__dict__查看

返回字典,key为属性名,value为属性值。

demo:

import math

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def move(self, x, y):
        self.x = x
        self.y = y

    def resect(self):
        self.move(0, 0)

    def cal_distence(self, other_point):
        return math.sqrt((self.x - other_point.x) ** 2 + (self.y - other_point.y) ** 2)

print(Point.__dict__)
运行结果:
{'__module__': '__main__', '__init__': <function Point.__init__ at 0x0000016F891A6488>, 'move': <function Point.move at 0x0000016F8A24A950>, 'resect': <function Point.resect at 0x0000016F8A24A9D8>, 'cal_distence': <function Point.cal_distence at 0x0000016F8A24AA60>, '__dict__': <attribute '__dict__' of 'Point' objects>, '__weakref__': <attribute '__weakref__' of 'Point' objects>, '__doc__': None}

特殊的类属性:

函数名描述
类名._name_返回类的名字
类名._doc_返回类的文档字符串
类名._base_返回类的第一个父类
类名._bases_返回类的所有父类构成的元组
类名._module_返回类定义所以的模块
类名._class_返回实例所对应的类
类名._dict_返回类的字典属性

3.4类的交互

demo:

class Person:
    def __init__(self, name, aggressivity, life_value):
        self.name = name
        self.aggressivity = aggressivity
        self.life_value = life_value

    def attack(self, dog):
        dog.life_value -= self.aggressivity


class Dog:
    def __init__(self, name, breed, aggressivity, life_value):
        self.name = name
        self.breed = breed  # 品种
        self.aggressivity = aggressivity
        self.life_value = life_value

    def bite(self, people):
        people.life_value -= self.aggressivity


per = Person('Bob', 10, 1000)
dog = Dog('Wawa', 'Husky', 20, 500)
print(dog.life_value)
per.attack(dog)
print(dog.life_value)
运行结果:
500
490

3.5类命名空间与对象、实例的空间

创建一个类就会创建一个类的名称空间,用来存储我们定义的所有变量名,这些名字就是属性。

类的属性分为:

  • 静态属性:

    直接在类中定义的变量

  • 动态属性:

    在类中定义的方法

静态属性是共享给所有对象的,动态属性是绑定到所有对象的。

demo:

class Person:
    face = 'aaa'
    def __init__(self, name, aggressivity, life_value):
        self.name = name
        self.aggressivity = aggressivity
        self.life_value = life_value

    def attack(self, dog):
        dog.life_value -= self.aggressivity

per = Person('Bob', 10, 1000)
per1=Person('Tom',23,1000)
print(id(per.face))
print(id(per1.face))
print(per.attack)#注意,这里都是没有加括号的
print(per1.attack)
print(Person.attack)
运行结果:
2631036987240
2631036987240
<bound method Person.attack of <__main__.Person object at 0x00000264973E5940>>
<bound method Person.attack of <__main__.Person object at 0x00000264973E5E80>>
<function Person.attack at 0x000002649740A950>

3.6类的继承

类的三大特性:继承、多态、封装。

在面向对象编程中,当我们定义一个新的类时,可以从某个现有的类继承,新的类就被称为子类(Sub Class),而被继承的类则被称为基类,父类,超类(Base Class,Father Class ,Super Class)。

demo1:

class Animal(object):
    def run(self):
        print('Animal is runing.')

class Dog(Animal):
    pass

class Cat(Animal):
    pass

dog=Dog()
cat=Cat()
print(dog.run())
print(cat.run())
运行结果:
Animal is runing.
None
Animal is runing.
None

demo2:

class Animal(object):  # 定义父类
    def run(self):
        print('Animal is runing.')

class Animal2:
    pass


class Dog(Animal):  # 单继承
    pass


class Cat(Animal):  # 单继承
    pass


class Huaky(Animal, Animal2):  # 多继承,用逗号隔开
    pass

print(Dog.__bases__)
print(Huaky.__bases__)
print(Animal.__bases__)
print(Animal2.__class__)
运行结果:
(<class '__main__.Animal'>,)
(<class '__main__.Animal'>, <class '__main__.Animal2'>)
(<class 'object'>,)
<class 'type'>

如果不指定基类,Python会默认继承object类。object是所有Python的基类,提供一些常见方法的实现。

demo3:

多态,当子类与父类存在相同的方法时,子类的方法会覆盖父类的方法。

定义一个类的时候,实际上就是定义了一种数据类型。我们自定义的数据类型和Python自带的数据类型,如str,int等什么区别。

class Animal(object):  # 定义父类
    def run(self):
        print('Animal is runing.')

class Dog(Animal):  # 单继承
    def run(self):
        print('Dog is Running')

dog=Dog()
print(dog.run())
运行结果:

demo4:

class Animal(object):  # 定义父类
    def run(self):
        print('Animal is runing.')
        
class Dog(Animal):  # 单继承
    def run(self):
        print('Dog is Running')

dog=Dog()
li=[]
print(isinstance(li,list))#isinstance()用来判断数据类型
print(isinstance(dog,Dog))
print(isinstance(dog,Animal))
运行结果:
True
True
True

对于一个变量,我们只需知道他的父类型,无需知道子类型,就可以放心调用相关的方法。运行时的具体方法时作用在子类型上还是父类型上,由运行对象决定。也即是说,调用时只管调用,不管细节,当我们新增一个子类时,只要保证相关的方法编写正确,就不用管原来的代码是如何调用的。

“开闭”原则:

  • 对拓展开放:允许新增子类
  • 对修改封闭:不需要修改依赖父类类型的函数。

demo5:

class Student:
    school = 'zucc'

    def __init__(self, name, score):
        self.name = name
        self.score = score

    def find_score(self):
        print(self.name, ':', self.score)

stu1=Student('Bob',98)
print(stu1.score)
stu1.score=50
print(stu1.score)
运行结果:
98
50

从这里可以看出,外部代码可以修改实例的属性(name,score)。如果要让内部的属性不被外部访问,可以在属性前面加两个下划线。

在Python中,实例的变量名如果以双下划线开头,就变成了一个私有变量,只有内部可以访问,外部不能访问。

demo6:

class Student:
    school = 'zucc'

    def __init__(self, name, score):
        self.name = name
        self.__score = score

    def find_score(self):
        print(self.name, ':', self.__score)


stu1 = Student('Bob', 98)
print(stu1.score)
stu1.score = 50
print(stu1.score)
运行结果:
Traceback (most recent call last):
  File "D:/PyCharm/BClass/PXClass/2019-7-29/daylystudy.py", line 153, in <module>
    print(stu1.score)
AttributeError: 'Student' object has no attribute 'score'

demo7:

class Student:
    school = 'zucc'

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

    def get_name(self):
        return self.__name

    def get_score(self):
        return self.__score

    def find_score(self):
        print(self.__name, ':', self.__score)


stu1 = Student('Bob', 98)
print(stu1.get_score())
stu1.score = 50
print(stu1.get_score())
运行结果:
98
98

3.7类的封装

隐藏对象的属性和实现细节仅对外提供公共访问的方式。

这样做的优点在于:

  1. 可以将变化隔离。
  2. 便于使用
  3. 提高安全性
  4. 提高复用性

封装的原则:将不需要对外提供的内容隐藏起来。隐藏属性,提供公共方法对其进行访问。

demo:

class Student:
    school = 'zucc'

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

    def get_name(self):
        return self.__name

    def get_score(self):
        return self.__score

    def set_score(self,score):
        if 0<=score<=100:
            self.__score=score
        else:
            raise ValueError('Bad score')
    def find_score(self):
        print(self.__name, ':', self.__score)


stu1 = Student('Bob', 98)

print(stu1.find_score())
stu1.score=97
print(stu1.score)
print(stu1.find_score())
print(stu1.set_score(97))
print(stu1.find_score())
print(stu1._Student__score)
运行结果:
Bob : 98 #print(stu1.find_score())
None
97 #print(stu1.score)
Bob : 98 #print(stu1.find_score())
None
None #print(stu1.set_score(97))
Bob : 97 #print(stu1.find_score())
None #print(stu1._Student__score)
97

demo:

class Animal(object):  # 定义父类
    def run(self):
        print('Animal is runing.')

class Dog(Animal):  # 单继承
    def run(self):
        print('Dog is Running')

class Cat(Animal):  # 单继承
    pass

class Duck:
    def run(self):
        print('多态测试。')

dog = Dog()
cat = Cat()
duck = Duck()
dog.run()
cat.run()
duck.run()

def run_twice(animal):
    animal.run()
    animal.run()

run_twice(duck)
运行结果:
Dog is Running
Animal is runing.
多态测试。
多态测试。
多态测试。

鸭子类型不需要有严格的继承关系,一个对象,只要”看起来像鸭子,走路像鸭子“,也就是说,如果要编写现有对象的自定义版本,可以继承该对象,也可以创建一个外观和行为相似但与其没有任何关系的全新对象。

比方说,利用标准库中定义的各种“与文件类似”的对象,尽管这些对象的工作方式像文件,但他们没有继承内置文件对象的方法。

博主QQ:1031748759,欢迎批评指正!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值