文档章节

python入门(十) -- 面向对象

兴趣使然的程序员
 兴趣使然的程序员
发布于 2017/05/22 09:35
字数 2474
阅读 44
收藏 2

和Java、C++、C#一样,python也是一门面向对象的语言,一样有以下相似的概念:

  • 类:用来描述具有相同属性和方法的对象的集合
  • 类变量:行为类似于Java中的static变量,在类中直接声明
  • 实例变量:相当于Java中的非static的成员变量,在构造方法中声明
  • 继承:和Java中相同,一个派生类可以继承基类的字段和方法,是“is-a”关系
  • 多重继承:和Java中不同,python允许多重继承
  • 方法重写:子类可以继承自父类的方法
  • 实例化:创建类的实例
  • 方法:类中可以定义函数
  • 对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法

1、类的定义:

# 定义类
class User:
    # 声明并给类变量赋值
    age = 15

    # 构造方法,可以带参数,python中实例变量在构造方法中声明
    def __init__(self, username, password):
        # 实例变量
        self.userName = username
        self.password = password

    # 无法在构造方法以外的方法中声明实例变量
    def f(self):
        # 在这里写self.gender = 'male'是不对的
        print(self.age)
        return 'hello world'
# 创建类的实例,如果想要后面的参数变为可选,可参考一般方法,设定默认值即可
x = User('cai', '258456')
# 调用实例变量
print(x.age)
print(x.userName)
print(x.password)
# 调用方法
x.f()

需要注意的是:

  • 类变量在类中声明,实例变量在__init__(self)构造方法中声明
  • 类的方法和一般的函数的差别在于,类的方法的第一个参数必须是self,self和Java中的this类似,指代当前对象实例(注意不是当前类)
  • __init__构造方法可以带其他参数,如果想要实现“参数可选”的话,参考之前说明的普通方法,给__init__构造方法的参数设定默认值即可:
# 定义类
class User:
    # 构造方法
    def __init__(self, username='cai', password='258456'):
        self.userName = username
        self.password = password

# 创建类的实例
x = User()
  • 类变量和实例变量的差别在于,类变量如果不改变引用,每个实例操作同一个类变量,如果改变引用,每个实例操作各自的副本。而实例变量每个实例并不相关。这就导致了行为表现的不同:

# 定义类
class User:
    # 类变量,每个对象都操作这个变量
    list = []
    age = 15

    # 构造方法
    def __init__(self, username='cai', password='258456'):
        self.userName = username
        self.password = password
        self.otherList = []

# 创建类的实例
x = User()
y = User()
x.list.append(15)
x.otherList.append(16)
x.age = 16
# 由于是同一个对象,所以x和y的list指向相同的地址,修改一个全都改变
print(y.list)
# 由于每次都是新的对象,所有修改x不会影响y
print(y.otherList)
# 并且x.age = 16声明了新的对象,不再关联原来的类变量,所以不影响y
print(y.age)


和Java中的“静态变量”的差别在于,Java中对静态变量进行操作时,操作的是静态变量本身,而不是副本。作为一门动态语言,python中并没有真正的“静态”(类变量只能共享值,不能共享引用,一旦实例更改了引用,就和类变量脱离了关系),只有类似于静态的行为。在python中也可以直接使用类名.变量来使用变量,而这样使用导致的效果也和Java中差别巨大:

# 创建类的实例
x = User()
y = User()
x.age = 16
# 直接使用类.类变量
User.age = 5
# 返回16 5 5
print(x.age, y.age, User.age)
x.list.append(99)
User.list = [88]
# 受影响,返回[88] [88] [88]
print(x.list, y.list, User.list)
# x改变了引用
x.list = [11]
User.list = []
# 不受影响,返回[11] [] []
print(x.list, y.list, User.list)
# 直接使用类.实例变量,虽然没有报错但是不会有任何效果
User.userName = 5

Java中静态变量的操作:

public class Test {
    private static int num;
    public static void main(String[] args) {
        //直接用类.静态变量,而无需创建实例
        System.out.println(Test.num);
        Test test1 = new Test();
        Test test2 = new Test();
        //实例.静态变量的效果和类.静态变量没有任何差别
        //num“真正的只存在一个”,任意的修改都会影响所有对象
        test1.num = 10;
        System.out.println(test2.num);
    }
}

所以结论是:python中的类变量不是真正的静态变量,类变量只能共享值,不能共享引用,一旦实例更改了引用,就和类变量脱离了关系。

注意下面两种写法的差异:

class ClassA:
    count = 0
    def __init__(self):
        # 使用对象引用
        self.count+=1

x = ClassA()
y = ClassA()
x.count+=1
# 返回2
print(x.count)
# 返回1
print(y.count)
class ClassA:
    count = 0
    def __init__(self):
        # 使用类引用
        ClassA.count+=1

x = ClassA()
y = ClassA()
x.count+=1
# 返回3
print(x.count)
# 返回2
print(y.count)

所以,如果想要实现和Java中类似的静态变量的效果,需要注意:

  1. 使用时,必须只用类名.变量的方式调用
  2. 一旦更改了引用,就会导致该对象的类变量的表现变为和普通变量相同。

2、继承与多继承

python中的实现继承需要注意:

  • 格式为:
    class DrivedClass(BaseClass1, BaseClass2, BaseClass3):
  • 基类和派生类必须在同一个作用域内,比如在同一个包下的不同子包中
  • 和Java中不同,python中基类的构造方法不会被自动调用,需要在派生类中显式调用。调用基类的方法时,使用基类名.方法名(self,参数列表)的形式调用

python中有几个用于继承的函数:

  • isinstance()用于检查实例类型,或者类继承
  • issubclass()只用于检查类继承
  • type()只用于检查实例类型

例如:

class ClassA:
    pass


# ClassB是ClassA的子类
class ClassB(ClassA):
    pass

# True,注意第一个参数是对象实例,不是类。第二个参数是类
print(isinstance(ClassB(), ClassA))
# True,注意两个参数都是类
print(issubclass(ClassB, ClassA))
# False,参数是类
print(type(ClassB) == ClassA)

3、私有属性、私有方法的定义

python中没有访问修饰符,如果想要实现“私有变量”、“私有方法”的话,需要在变量名、方法名加上两个下划线__作为标识,例如:

class Mapping:
    def __init__(self, iterable):
        self.items_list = []
        # 在初始化函数中使用update函数的副本进行初始化
        self.__update(iterable)

    def update(self,iterable):
        for item in iterable:
            self.items_list.append(item)

    __update = update


class MappingSubClass(Mapping):
    # 由于使用了副本,重写update方法并不会影响到基类的初始化
    def update(self, keys, values):
        for item in zip(keys, values):
            self.items_list.append(item)

4、global和nonlocal

python引用变量的顺序: 当前作用域局部变量->外层作用域变量->当前模块中的全局变量->python内置变量。

global 语句用以指明某个特定的变量为全局作用域,并重新绑定它。nonlocal 语句用以指明某个特定的变量为封闭作用域,并重新绑定它。

简单来说,就是允许在方法内操作方法外部的变量,而不经过参数传递(Java中方法不能调用除了静态成员以外的任何方法外的变量,python中有所不同,没有静态的概念,但是可以通过global实现类似的效果)。

def scope_test():
    def do_local():
        # 不使用nonlocal时,对spam的改变不会影响外部的同名变量spam
        spam = "local spam"

    def do_nonlocal():
        # 使用nonlocal修饰,使在do_nonlocal()方法的范围内,可以操作scope_test()内的spam
        nonlocal spam
        spam = "nonlocal spam"

    def do_global():
        # 使用global修饰,使在当前范围内,可以操作全局的spam
        global spam
        spam = "global spam"
    # scope_test范围的spam
    spam = "test spam"
    do_local()
    # 这个和下面三个print,使用的都是scope_test范围的spam
    # 不变
    print("After local assignment:", spam)
    do_nonlocal()
    # 改变为nonlocal spam
    print("After nonlocal assignment:", spam)
    do_global()
    # 依然是nonlocal spam,因为do_global改变的是更外部的spam
    print("After global assignment:", spam)

scope_test()
# 改变为global spam
print("In global scope:", spam)

简单地说,即nonlocal使方法可以操作其上一级作用域的同名对象,global使方法可以操作最外层作用域里的同名对象。

5、自定义迭代器

可以通过重写__iter__(self)方法和__next__(self)方法自定义迭代器。__iter__(self)方法返回一个带有__next()__对象,如果已经定义了__next()__,__iter__()方法返回self即可。__next()__方法定义迭代的规则。例如:

class Reverse:
    
    def __init__(self, data):
        self.data = data
        self.index = len(data)
        
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.index == 0:
            raise StopIteration
        self.index -= 1
        return self.data[self.index]

6、自定义生成器

Generator用于创建迭代器。需要返回数据的时候使用yield语句。例如:

def reverse(data):
    for index in range(len(data)-1, -1, -1):
        yield data[index]

for char in reverse('golf'):
    print(char)

 

7、和其他语言的不同

在 Python 里请不要使用属性(attributes)读取方法(getters 和 setters)。如果你之前学过其它语言(比如 Java),你可能会想要在你的类里面定义属性读取方法。请不要这样做,直接使用属性就可以了,就像下面这样:

>>> class Student(object):
...     def __init__(self, name):
...         self.name = name
...
>>> std = Student("Kushal Das")
>>> print(std.name)
Kushal Das
>>> std.name = "Python"
>>> print(std.name)
Python

7.1、Properties 装饰器

你可能想要更精确的调整控制属性访问权限,你可以使用 @property 装饰器,@property 装饰器就是负责把一个方法变成属性调用的。

下面有个银行账号的例子,我们要确保没人能设置金额为负,并且有个只读属性 cny 返回换算人名币后的金额。

#!/usr/bin/env python3

class Account(object):
    """账号类,
    amount 是美元金额.
    """
    def __init__(self, rate):
        self.__amt = 0
        self.rate = rate

    @property
    def amount(self):
        """账号余额(美元)"""
        return self.__amt

    @property
    def cny(self):
        """账号余额(人名币)"""
        return self.__amt * self.rate

    @amount.setter
    def amount(self, value):
        if value < 0:
            print("Sorry, no negative amount in the account.")
            return
        self.__amt = value

if __name__ == '__main__':
    acc = Account(rate=6.6) # 基于课程编写时的汇率
    acc.amount = 20
    print("Dollar amount:", acc.amount)
    print("In CNY:", acc.cny)
    acc.amount = -100
    print("Dollar amount:", acc.amount)

运行程序:

此处输入图片的描述

 

© 著作权归作者所有

共有 人打赏支持
兴趣使然的程序员
粉丝 23
博文 112
码字总数 87412
作品 0
深圳
程序员
私信 提问
PYTHON面向对象章节深入讲解-张明阳-专题视频课程

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/a2011480169/article/details/83581748 PYTHON面向对象章节深入讲解—73人已学习 课程介绍 介绍了类和面向对象...

安静的技术控
08/24
0
0
Python实战开发之Pyramid Web框架在商城项目中的应用实战资料

Python实战开发之Pyramid Web框架在商城项目中的应用实战资料 第一讲:Python基础和入门介绍(Web开发基础) 第二讲:Python语言基础(运算符与表达式,控制流,函数,模块) 第三讲:Pytho...

神人
2013/07/15
2.9K
4
编程语言太难学?只因你还没试过Python!

Python语言可能是第一种即简单又功能强大的编程语言。它不仅适合于初学者,也适合于专业人员使用,更加重要的是,用Python编程是一种愉快的事。本身将帮助你学习这个奇妙的语言,并且向你展示...

极客学院
2015/05/18
2.1K
7
Python 2.7.4、3.2.4 和 3.3.1 发布

Python 今天发布了三个更新版本,分别是 Python 2.7.4 、Python 3.2.4 和 Python 3.3.1。改进内容和下载地址请点链接进入。 Python (发音:[ 'paiθ(ə)n; (US) 'paiθɔn ]n.蟒蛇,巨蛇 ),是...

红薯
2013/04/07
4.3K
13
十问Python,所有关于Python的疑问,由十年pythoner给你作答!

为什么要学习Python编程语言?哪些人适合学习Python? 先回答第一个被初学编程的朋友问到最多的问题,为什么要学习Python编程语言? 答:现在信息更新的非常快速,又迎来了大数据的时代, 各...

Python新世界
07/20
0
0

没有更多内容

加载失败,请刷新页面

加载更多

[LintCode] Serialize and Deserialize Binary Tree(二叉树的序列化和反序列化)

描述 设计一个算法,并编写代码来序列化和反序列化二叉树。将树写入一个文件被称为“序列化”,读取文件后重建同样的二叉树被称为“反序列化”。 如何反序列化或序列化二叉树是没有限制的,你...

honeymose
今天
5
0
java框架学习日志-7(静态代理和JDK代理)

静态代理 我们平时去餐厅吃饭,不是直接告诉厨师做什么菜的,而是先告诉服务员点什么菜,然后由服务员传到给厨师,相当于服务员是厨师的代理,我们通过代理让厨师炒菜,这就是代理模式。代理...

白话
今天
23
0
Flink Window

1.Flink窗口 Window Assigner分配器。 窗口可以是时间驱动的(Time Window,例如:每30秒钟),也可以是数据驱动的(Count Window,例如:每一百个元素)。 一种经典的窗口分类可以分成: 翻...

满小茂
今天
18
0
my.ini

1

architect刘源源
今天
16
0
docker dns

There is a opensource application that solves this issue, it's called DNS Proxy Server It's a DNS server that solves containers hostnames, if could not found a hostname that mat......

kut
今天
17
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部