Python3面向对象基础

面向对象概述

面向对象
面向对象的世界,引入了对象的概念,对象具有属性:数据,对象具有过程或者方法:成员函数。成员函数的作用就是处理属性。

例子
对象:Car
属性:fuel_level, isSedan, speed, steering_wheel, coordinates
方法:accelerate(), takeleft()

每个对象都有自己的内存地址或身份,对象也就是类的实例。

对象

它们表示所有开发的应用程序内的**实体**
实体之间可以通过交互来解决现实世界的问题
例如:Person是实体,而Car也是实体。Person可以驾驶Car,从一个地方开到另一个地方。

**类可以帮助开发人员表示现实世界中的实体**
类可以定义对象的属性和行为。属性是数据成员,行为由成员函数表示。
类包含了构造函数,这些函数的作用是为对象提供初始状态。
类就像模板一样,非常易于重复使用

方法

它们表示对象的行为
方法可以对属性进行处理,从而实现所需的功能。
class Person(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def get_person(self,):
        return "<Person (%s, %s)>" % (self.name, self.age)

# 定义一个人
p = Person("John", 32)
print("Type of Object:", type(p), "Memory Address:", id(p))
Type of Object: <class '__main__.Person'> Memory Address: 2252412092992

封装

对象的行为对于外部世界来说是不可见的,或者说对象的状态信息是私密的。
客户端不能通过直接操作来改变对象的内部状态。相反,客户端需要通过发送消息来请求对象改变其内部状态。对象可以根据请求的类型,通过特定的成员函数(get或者set)改变她们的内部状态,以做出相应的相应。
在Python中,封装(数据和方法的隐藏)的概念不是隐式的,因为他们没有提供封装所需的关键字(public private protected)。如果在变量或者函数名前面加上前缀__,就可以将其可访问性变为私有。

多态

对象根据输入参数提供方法的不同实现
不同类型的对象可以使用相同的接口 
python本身就是多态的

由不同的类实例化得到的对象,调用同一个方法,执行的逻辑不同
指出了对象如何通过他们共同的属性和动作来操作及访问,而不需要考虑它们具体的类。
表明了动态绑定的存在,允许重载及运行时类型确定和验证。
体现在调用不同对象的共同方法时,才能展现出来。
a = 'bright'
b = (1,2,3)
c = [3,4,5,6,7]
print(a[1],b[0],c[2])
r 1 5
class H2O:
    def __init__(self,name,temperature):
        self.name=name
        self.temperature=temperature
    def turn_ice(self):
        if self.temperature < 0:
            print('[%s]温度太低结冰了' %self.name)
        elif self.temperature > 0 and self.temperature < 100:
            print('[%s]液化成水' %self.name)
        elif self.temperature > 100:
            print('[%s]温度太高变成了水蒸气' %self.name)

class Water(H2O):
    pass
class Ice(H2O):
    pass
class Steam(H2O):
    pass

w1=Water('水',25)
i1=Ice('冰',-20)
s1=Steam('蒸汽',3000)

# 下面三个对象调用了相同的方法,但是得到的结果却不一样。
w1.turn_ice()
i1.turn_ice()
s1.turn_ice()
[水]液化成水
[冰]温度太低结冰了
[蒸汽]温度太高变成了水蒸气

继承

继承可以表示一个类可以继承父类的大部分功能
继承被描述为一个重要基类中定义的功能并允许对原始软件的实现进行独立扩展的选项。
继承可以利用不同的对象之间的关系建立层次结构。

继承的方式

通过继承建立了派生类与基类之间的关系,它是一种'是'的关系,比如白马是马,人是动物。

当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好,比如老师是人,学生是人

class A:
    def a1(self):
        print('a1')

class B(A):
    def b(self):
        print('b')
        
b = B()
print(b.a1())
print(b.b())
a1
None
b
None
class Dad:
    '这个是爸爸类'
    money=10
    def __init__(self,name):
        print('父类')
        self.name=name
    def hit_son(self):
        print('%s 正在打儿子' % self.name)

class Son(Dad):
    money = 1000000009
    def __init__(self,name,age):
        self.name=name
        self.age=age

    def hit_son(self):
        print('来自子类')

s1 = Son('Jerry',12)
print(s1.money)
print(Dad.money) # 子类在自己里面定义了与父类同名的属性,并没有覆盖父类的属性
s1.hit_son()
print(s1.__dict__)
1000000009
10
来自子类
{'name': 'Jerry', 'age': 12}
class A:
    def test(self):
        print("A")

class B(A): # 单继承
    # def test(self):
    #     print("B")
    pass

class C(A):
    def test(self):
        print("C")
    pass

class D(B):
    # def test(self):
    #     print("D")
    pass

class E(C):
    # def test(self):
    #     print("E")
    pass

class F(D,E): # 多继承
    # def test(self):
    #     print("F")
    pass

f1 = F()
f1.test()
# 新式类查找顺序 F >> D >> B >> E >> C >> A
# 经典类查找顺序 F >> D >> B >> A >> E >> C

print(F.__mro__)

## 如果继承关系为菱形结构,那么属性的查找方式有两种,分别是:深度优先和广度优先
## 经典类时,要查找的属性不存在时,多继承会按照深度优先的方式查找
## 新式类时,要查找的属性不存在时,多继承会按照广度优先的方式查找,最后找到object
C
(<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
class Vehicle:
    def __init__(self,name,speed,load,power):
        self.name = name
        self.speed = speed
        self.load = load
        self.power = power

    def run(self):
        print("开动啦。")


class Benz(Vehicle):
    def __init__(self,name,speed,load,power,color):
        # Vehicle.__init__(self,name,speed,load,power) # 使用指定类名方式调用了父类方法
        # super(__class__.self).__init__(name,speed,load,power) # 使用super(__class__.self)方式
        super().__init__(name,speed,load,power) # 采用super()不传参数的方法
        self.color=color

    def show_info(self):
        print(self.name,self.speed,self.load,self.power,self.color)

    def run(self):
        # Vehicle.run(self) # 使用指定类名称的方式,调用了父类方法
        super().run()
        print("%s %s 出发啦." % (self.color,self.name))


car1 = Benz("GLK 300","120","5人","2.4T","black")
car1.show_info()
car1.run()
GLK 300 120 5人 2.4T black
开动啦。
black GLK 300 出发啦.

抽象

她提供了一个简单的客户端接口,客户端可以通过该接口与类的对象进行交互,并可以调用该接口中定义的各个方法。
她将内部类的复杂性抽象为一个接口,这样客户端就不需要知道内部实现了。
class Adder:
    def __init__(self):
        self.sum = 0
    def add(self, value):
        self.sum += value
        
acc = Adder()
    
for i in range(101):
    acc.add(i)
    
print(acc.sum)
5050

组合

他是一种将对象或类组合成更为复杂的数据结构或软件实现的方法
在组合中,一个对象可用于调用其它模块中的成员函数,这样一来,无需通过继承就可以实现基本功能的跨模块使用。

组合的方式

用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如教授有生日,教授教python和linux课程,教授有学生s1、s2、s3...

当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好

class A(object):
    def a1(self):
        print('a1')
class B(object):
    def b(self):
        print('b')
        A().a1()  # 实现跨类组合
objectB = B()
objectB.b()
b
a1

谈谈你对面向对象的理解?

  • 面向对象,首先我们要理解对象,什么是对象?对象是特征与技能的结合体。特征即变量,技能即函数,对象就是变量和函数的结合体,在python中既有面向对象编程,
  • 也有面向过程编程,在所做项目中需要用到哪种更加便捷就用那种编程方式,学面向对象的人,正常最常听到的一句话就是“万物皆对象”,我们编程就是将现实中的事物,
  • 抽象成程序中的对象,用特征和技能去表现出对象。

Python面向对象中的继承有什么特点?

  • 减少代码重用,可以多继承,缺点就是把子类与父类强耦合到一起

面向对象深度优先和广度优先是什么?

  • 深度优先主要体现在经典类在菱形继承的背景下,查找属性的方式
  • 广度优先主要体现在新式类在菱形继承的背景下,查找属性的方式,

面向对象中super的作用?

  • super在子类派生出新的方法中重用父类的功能
  • 注意:super()的返回值是一个特殊的随想,该对象专用用来调用父类的属性

列举面向对象中特殊成员(带双下划线的特殊方法)

__new__ 可以调用其他类的构造方法或者直接返回别的对象来作为本类的实例
__init__            负责将类的实例化
__call__            对象后面加括号,触发执行       
__str__             print打印一个对象时  __unicode__
__repr__            和__str__
__doc__             类的注释,改属性是无法继承的
__getattr__         在使用调用属性(方式.属性)不存在的时候触发
__setattr__         添加/修改属性会触发它的执行
__delattr__         删除属性的时候会触发
__delete__          采用del删除属性时,触发   

静态方法和类方法区别?

  • 静态方法: 相当于普通函数 staticmethod
  • 类方法: 通过类调用,第一个参数默认是类本身 classmethod

反射

通过字符串来操作类与对象的属性,这种操作称为反射
hasattr(obj,某种属性)
不知道有没有某种方法时,进行的判断
getattr(obj,某种属性,None)
查看某种属性,没有属性,可以设置为NONE,有就返回值
setattr(obj,某种属性,属性对应的值)
添加obj对象的某种属性,及其值
delattr(obj,某种属性,属性对应的值)
删除obj对象的某种属性,及其值

面向对象中方法的定义之函数讲解

什么是函数?

在程序中,函数就是具备某一功能的工具,事先将工具准备好即函数的定义;遇到应用场景拿来就用即函数的调用;函数必须遵循先定义后调用的原则

为什么要用函数

不用函数的问题是:程序冗长  程序扩展性查   程序的可读性查

函数的返回值

  1. 什么是返回值:返回值是一个函数的处理结果

  2. 为什么要有返回值:如果需要在程序中拿到函数的处理结果,做进一步的处理,则需要函数必须有返回值

  3. 函数返回值的应用:函数的返回值用return定义;格式: return x;

    【注意】:

    1. return是一个函数结束的标志,可有多个return,但只要执行一次,函数就会结束运行

    2. return的返回值五类型限制,可任意数据类型

    3. 返回值无个数限制,可用逗号分开多个任意类型的返回值

    4. 0个:返回None 1个:返回值就是值本身

    5. 多个:返回值为元组

    6. 不写return,默认会在最后return None

def max2(x,y): #x=3000,y=2000
     if x > y:
         return x #return 3000
     else:
         return y #reuturn 2000
res=max2(3000,2000)
annual_salary=res * 12
print(annual_salary)         

调用函数

  1. 什么是调用函数

    函数名() 即调用,会执行函数体代码

  2. 为何要调用?

    执行函数的功能

  3. 函数调用的三种形式

def count(x):
return x
1.普通:count(x)
2.放在运算中:count(x) * 10
3.作为参数调用:count(count(x))

函数参数的应用:

形参:在函数定义阶段括号内定义的参数,称为形参,本质是变量名

实参:在函数调用阶段括号内传入的值,称为实参,本质是变量的值

详细分类

  1. 位置形参:在函数定义阶段,按从左到右的顺序依次定义的参数
    特点:必须被传值,不可多,不可少
    位置实参:在函数调用阶段,按从左到右的顺序依次定义的参数
    特点:按照位置为对应的形参依次传值

  2. 关键字实参:按key:value传值,称关键字实参
    特点:可以打破位置的限制,但仍能为指定的形参赋值
    注意:
    1.可以混用位置参数与关键字实参,但位置实参必须 在前面
    2.可以混用,但不能对一个形参重复赋值

  3. 默认参数:在函数定义阶段,就已经为形参赋值
    特点:在定义阶段已被赋值,意味着在调用时可以不为其赋值
    注意:
    1. 位置形参必须放在默认参数前面
    2. 默认参数的值只在定义阶段赋值一次,即定义时就固定写死了
    3. 默认参数的值通常应该定义不可变类型

      总结:实参的应用:可取决于个人的习惯
    
     形参的应用:
         大多数情况下调用值都一样:默认参数
         大多数情况下调用值不一样:位置参数
    
  4. 可变长参数:指的是在函数调用时:传入的参数个数可以不固定,而调用函数时,传值的方式无非两种,一种是位置实参,另一种是关键实参,所以对应着形参也必须有两种解决方案来分别接收溢出的位置实参(*)与关键字实参(**)

    位置参数(*)
    ①.形参中接收溢出的位置实数,然后存成元组的形式,赋值给后的变量
    ②.实参中的参数也可以带
    实参中带
    会将该参数的值循环取出,打散成位 置参数,但凡带实参,它就是位置实参,应该打散成位置实参看
    约定俗成:形参中的变量名的写法都是args
    关键字参数()**
    ①.形参中的会将溢出的关键字实参全部接收,然后存成字典的形式,赋值给后的变量名
    ②.实参中带会将该参数的值循环取出,打散成关键字实数,但凡带的实参, 它就是关键字实参,应该打散成位关键字参看。后必须是字典
    约定俗成:形参中的变量名的写法都是kwargs

如何用函数

del 函数名(参数1,参数2,...):
    函数功能的描述信息
    参数1描述
    参数2描述
    return:返回值
    代码块
    return:返回值

函数的定义和调用阶段

(1)函数的使用必须遵循先定义,后调用原则
(2)没有事先定义函数,而直接引用函数名,就相当于在引用一个不存在的变量名
(3)函数定义阶段:只检测函数体的语法,不执行函数体代码
(4)函数调用阶段执行代码

函数定义的三种形式

  • 定义函数时的参数就是函数体接收外部传值的一种媒介,其实就是一个变量名

无参函数
在函数定义阶段括号内没有参数
注意:定义无参,意味着调用时也无需传入参数
应用:如果函数体的代码逻辑不需要依赖外部传入的值,必须定义无参函数

def func():
    print('hello world')
func()

有参函数
在函数定义阶段括号内有参数,称为有参函数
注意:定义时有参,意味着调用时也必须传入参数
应用:如果函数体代码逻辑需要依赖外部传入的值,必须定义成有参函数

def sum2(x,y):
    x=10
    y=20
    res=x+y
print(res)

sum2(10,20)
sum2(30,40)

空函数

def func():
    pass

函数调用

 (1)什么是调用函数

    函数名即调用函数,会执行函数体代码,直到碰到return结束或者一直运行完毕所有代码

 (2)为何要调用函数

    用函数的功能

 (3)函数调用的三种方式
def max2(x,y):
    if x > y:
        return x
    else:
        return y

#形式一:
max2(1,2)

#形式二:
res=max2(3000,2000) * 12
print(res)

#形式三:
res=max2(max2(1000,2000),3000)
print(res)  

Python小知识点补充

  • enumerate 函数可以同时遍历元素及其序号
字母 = ["A", "B", "C", "D"]
for i, j in enumerate(字母):
    print("Item {} is {}".format(i, j))
Item 0 is A
Item 1 is B
Item 2 is C
Item 3 is D
  • 自定义枚举类型
from enum import Enum
class Color(Enum):
    red = 1
    blue = 2
    green = 3

自定义枚举的应用

Color.red
<Color.red: 1>
type(Color.green)
<enum 'Color'>
isinstance(Color.red, Color)
True
list(Color)
[<Color.red: 1>, <Color.blue: 2>, <Color.green: 3>]

引用文献:

[1]http://www.cnblogs.com/luck-L/p/9145073.html
[2]http://www.cnblogs.com/quqinchao/p/qqchanshu.html
[3]http://www.cnblogs.com/ManyQian/p/9146526.html
[4]http://www.cnblogs.com/frx9527/p/python_oop3attr.html
[5]《Python设计模式》

posted @ 2018-06-07 15:28  既生喻何生亮  阅读(321)  评论(0编辑  收藏  举报