Python 进阶教程笔记

本文探讨了Python中的面向对象编程和函数式编程概念,通过实例展示了如何定义类、实例化对象、操作属性、继承与多态,以及使用高阶函数如map、reduce和filter。此外,还介绍了Python的网络编程和函数式编程技巧,如装饰器和闭包。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

面向对象编程

一、什么是面向对象编程

  • 程序设计的范式
  • 抽象并建立对象模型
  • 程序是不同对象相互调用的逻辑

二、Python类的定义与实例化

在Python中,通过class关键字定义一个类,比如我们需要定义一个人的类。按照 Python 的编程习惯,类名以大写字母开头。因此可以这样定义:

class Person:  pass

注意,在这个Person类的定义里面,并没有继承任何类,除了这样定义以外,还可以有以下两种定义方式。

class Person(): pass  
class Person(object):  pass

这三种情况有什么区别呢?在Python3中,是没有区别的,但是在Python2中,则有一定的区别。

在Python2中,对于第一种定义的方法,Person类只有有限的几个内建函数'__doc__', '__module__', 'name',而对于第二种、第三种定义的方法,则会继承Python object对象的更多的内建函数,可以更便捷的操作对象。这是Python2版本的差异。在Python3中,我们只需要知道这三种方式都可以定义一个类即可。

定义了类之后,就可以对类进行实例化了,实例化是指,把抽象的类,赋予实物的过程。比如,定义好Person这个类后,就可以实例化多个Person出来了。 创建实例使用类名+(),类似函数调用的形式创建:

class Person(object):  pass 
xiaohong = Person()
xiaoming = Person()

任务

请练习定义一个动物类,并创建出两个实例dog, cat,打印实例,再比较两个实例是否相等。

要打印实例,直接使用print语句;

要比较两个实例是否相等,用==操作符。

参考答案
class Animal(object):  
	pass

dog = Animal()
cat = Animal()
print(dog) print(cat) print(dog == cat)

三、实例属性的定义

虽然前面我们已经通过Person类创建出xiaoming、xiaohong等实例,但是这些实例看上去并没有任何区别。在现实世界中,一个人拥有名字、性别、年龄等等的信息,在Python中,可以通过以下的方式赋予实例这些属性,并且把这些属性打印出来。

xiaohong.name = 'xiaohong'
xiaohong.sex = 'girl'
xiaohong.age = 13

print(xiaohong.name)
print(xiaohong.sex)
print(xiaohong.age)

除此以外,这些属性也可以和普通变量一样进行运算。比如xiaohong长大了一岁:

xiaohong.age = xiaohong.age + 1

任务

请定义一个动物类,并创建出两个实例dog, cat,分别赋予不同的名字和年龄并打印出来。

参考答案
class Animal(object):
    pass

dog = Animal()
cat = Animal()
dog.name = 'wangwang'
dog.age = 1
cat.name = 'mimi'
cat.age = 3

四、实例属性的初始化

通过前面的方式定义一个实例的属性非常方便,但也有一些问题。 首先,如果定义属性的过程中使用了不同的属性名字,比如性别,前者使用了sex,后者使用了gender,那对于一个类的不同实例,存储一个信息就用了两个不同的属性,在后面将会难以维护。 其次,名字、性别、年龄等等,都是人的基本信息,在抽象一个类的时候,理应包含这些信息。 在定义 Person 类时,可以为Person类添加一个特殊的__init__()方法,当创建实例时,__init__()方法被自动调用,我们就能在此为每个实例都统一加上以下属性:

class Person(object):
    def __init__(self, name, sex, age):
        self.name = name
        self.sex = sex
        self.age = age

需要注意的是,__init__() 方法的第一个参数必须是 self(也可以用别的名字,但建议使用习惯用法),后续参数则可以自由指定,和定义函数没有任何区别。 定义类后,就可以相应的实例化对象了,需要注意的是,在实例化的时候,需要提供除self以外的所有参数。

xiaoming = Person('Xiao Ming', 'boy', 13)
xiaohong = Person('Xiao Hong', 'girl', 14)

而访问这些属性的方式和之前的一样:

print(xiaohong.name)
print(xiaohong.sex)
print(xiaohong.age)
# 但当访问不存在的属性时,依然会报错
print(xiaohong.birth)

要特别注意的是,初学者定义__init__()方法常常忘记了 self 参数,比如如下的定义:

class Person(object):
    def __init__(name, sex, age):
        pass

这种情况下,如果还是如下实例化,将会报错。

xiaoming = Person('Xiao Ming', 'boy', 13)
xiaohong = Person('Xiao Hong', 'girl', 14)

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __init__() takes 3 positional arguments but 4 were given

任务

请定义一个动物类,抽象出名字、年龄两个属性,并实例化两个实例dog, cat

参考答案
class Animal(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age

dog = Animal('wangwang', 1)
cat = Animal('mimi', 3)
print(dog.name)
print(dog.age)
print(cat.name)
print(cat.age)

五、类属性

类和实例对象是有区别的,类是抽象,是模板,而实例则是根据类创建的对象,比如类:动物,只是一个抽象,并没有动物的详细信息,而猫、狗等,则是具体的动物,是类的对象。

在前面,实例对象绑定的属性只属于这个实例,绑定在一个实例上的属性不会影响其它实例;同样的,类也可以绑定属性,但是类的属性不属于任何一个对象,而是属于这个类。如果在类上绑定一个属性,则所有实例都可以访问类的属性,并且,所有实例访问的类属性都是同一个!也就是说,实例属性每个实例各自拥有,互相独立,而类属性有且只有一份。

定义类属性可以直接在 class 中定义,比如在前面的Animal类中,加入地域的类属性:

class Animal(object):
    localtion = 'Asia'
    def __init__(self, name, age):
        self.name = name
        self.age = age

在上面的代码中,localtion就是属于Animal这个类的类属性,此后,通过Animal()实例化的所有对象,都可以访问到localtion,并且得到唯一的结果。

dog = Animal('wangwang', 1)
cat = Animal('mimi', 3)
print(dog.localtion) # ==> Asia
print(cat.localtion) # ==> Asia
# 类属性,也可以通过类名直接访问
print(Animal.localtion) # ==> Asia

类属性也是可以动态添加和修改的,需要注意的是,因为类属性只有一份,所以改变了,所有实例可以访问到的类属性都会变更:

Animal.localtion = 'Africa'
print(cat.localtion) # ==>Africa
print(dog.localtion) # ==>Africa

任务

请给 Animal类添加一个类属性 count,每创建一个实例,count 属性就加 1,这样就可以统计出一共创建了多少个 Animal的实例。

参考答案
class Animal(object):
    count = 0
    def __init__(self, name, age):
        self.name = name
        self.age = age
        Animal.count += 1

dog = Animal('wangwang', 1)
print(Animal.count)
cat = Animal('mimi', 3)
print(Animal.count)
pig = Animal('panpan', 1)
print(Animal.count)

六、类属性和实例属性的优先级

可以看到,属性可以分为类属性和实例属性,那么问题就来了,如果类属性和实例属性名字相同时,会怎么样,这就涉及Python中类属性和实例属性的优先级的问题了。

我们可以做一个实验,在前面类定义的基础上,在实例属性中,也初始化一个localtion的属性。

class Animal(object):
    localtion = 'Asia'
    def __init__(self, name, age, localtion):
        self.name = name
        self.age = age
        self.localtion = localtion

接着我们初始化两个实例,并把localtion打印出来。

dog = Animal('wangwang', 1, 'GuangDong')
cat = Animal('mimi', 3, 'ChongQing')
print(dog.localtion) # ==> GuangDong
print(cat.localtion) # ==> ChongQing
print(Animal.localtion) # ==> Asia

可见,在类属性和实例属性同时存在的情况下,实例属性的优先级是要高于类属性的,在操作实例的时候,优先是操作实例的属性。

另外,当实例没有和类同名的时候,通过实例对象,依然可以访问到类属性。

class Animal(object):
    localtion = 'Asia'
    def __init__(self, name, age):
        self.name = name
        self.age = age

cat = Animal('mimi', 3)
print(cat.localtion) # ==> Asia

那通过实例,可不可以修改类属性呢?我们来尝试一下:

cat.localtion = 'Africa'
print(Animal.localtion) # ==> Asia

这里依然打印了Asia,可见通过实例是无法修改类的属性的,事实上,通过实例方法修改类属性,只是给实例绑定了一个对应的实例属性:

# 新增的实例属性
print(cat.localtion) # ==> Africa

因此,需要特别注意,尽量不要通过实例来修改类属性,否则很容易引发意想不到的错误。

任务

请把上节的 Animal类属性 count 改为 __count,再试试能否从实例和类访问该属性。

参考答案
把count改为私有__count,这样实例变量在外部无法修改__count
class Animal(object):
    __count = 0
    def __init__(self, name):
        Animal.__count = Animal.__count + 1
        self.name = name
        print(Animal.__count)

p1 = Animal('Cat')
p2 = Animal('Dog')

print(Animal.__count)

七、访问限制

并不是所有的属性都可以被外部访问的,这种不能被外部访问的属性称为私有属性。私有属性是以双下划线'__'开头的属性。

# 类私有属性
class Animal(object):
    __localtion = 'Asia'

print(Animal.__localtion)

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'Animal' has no attribute '__localtion'
# 实例私有属性
class Animal(object):
    def __init__(self, name, age, localtion):
        self.name = name
        self.age = age
        self.__localtion = localtion

dog = Animal('wangwang', 1, 'GuangDong')
print(dog.name) # ==> wangwang
print(dog.age) # ==> 1
print(dog.__localtion)

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Animal' object has no attribute '__localtion'

在外部访问私有属性将会抛出异常,提示没有这个属性。 虽然私有属性无法从外部访问,但是,从类的内部是可以访问的。私有属性是为了保护类或实例属性不被外部污染而设计的。

任务

请给Animal类的__init__方法中添加name和age参数,并把age绑定到__age属性上,看看外部是否能访问到。

参考答案
class Animal(object):
    def __init__(self, name, age):
        self.name = name
        self.__age = age

cat = Animal('Kitty', '3')

print(cat.name)
print(cat.__age)

八、定义实例方法

上面提到,私有属性没有办法从外部访问,只能在类的内部操作;那如果外部需要操作私有属性怎么办?这个时候可以通过定义类或者实例的方法来操作私有属性,本节课先来介绍实例方法。

实例的方法指的就是在类中定义的函数,实例方法的第一个参数永远都是self,self是一个引用,指向调用该方法的实例对象本身,除此以外,其他参数和普通函数是完全一样的。

class Person(object):

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

    def get_name(self):
        return self.__name

在上面的定义,name是实例的私有属性,从外部是无法访问的,而get_name(self) 就是一个实例方法,在实例方法里面是可以操作私有属性的,注意,它的第一个参数是self。

另外,__init__(self, name)其实也可看做是一个特殊的实例方法。

通过定义get_name(self)方法,在外部就可以通过这个方法访问私有属性了。

p = Person('Alice')
print(p.get_name()) # ==> Alice

注意,在外部调用实例方法时,是不需要显式传递self参数的。

另外,通过定义实例方法来操作私有属性的这种方法是推荐的,这种数据封装的形式除了能保护内部数据一致性外,还可以简化外部调用的难度。 当然,实例方法并不仅仅是为私有属性服务的,我们可以把和类的实例有关的操作都抽象成实例方法,比如:打印实例的详细信息等等。

class Animal(object):
    def __init__(self, name, age, localtion):
        self.name = name
        self.age = age
        self.localtion = localtion

    def get_info(self):
        return 'name = {}, age = {}, localtion = {}'.format(self.name, self.age, self.localtion)

dog = Animal('wangwang', 1, 'GuangDong')
print(dog.get_info())

任务

把Animal类的age、name、localtion定义成私有属性,并定义对应的方法修改和获取他们的值。

参考答案
class Animal(object):
    def __init__(self, name, age, localtion):
        self.__name = name
        self.__age = age
        self.__localtion = localtion

    def set_name(self, name):
        self.__name = name

    def get_name(self):
        return self.__name

    def set_age(self, age):
        self.__age = age

    def get_age(self):
        return self.__age

    def set_localtion(self, localtion):
        self.__localtion =localtion

    def get_localtion(self):
        return self.__localtion

九、定义类方法

在上面,为了操作实例对象的私有属性,我们定义了实例方法;同样的,如果需要需要操作类的私有属性,则应该定义类的方法。

默认的,在class中定义的全部是实例方法,实例方法第一个参数 self 是实例本身。

要在class中定义类方法,需要这么写:

class Animal(object):
    __localtion = 'Asia'
    def __init__(self, name, age):
        self.name = name
        self.age = age

    @classmethod
    def set_localtion(cls, localtion):
        cls.__localtion = localtion

    @classmethod
    def get_localtion(cls):
        return cls.__localtion

print(Animal.get_localtion()) # ==> Asia
Animal.set_localtion('Afica')
print(Animal.get_localtion()) # ==> Africa

和实例方法不同的是,这里有两点需要特别注意:

  1. 类方法需要使用@classmethod来标记为类方法,否则定义的还是实例方法
  2. 类方法的第一个参数将传入类本身,通常将参数名命名为 cls,上面的 cls.__localtion 实际上相当于Animal.__localtion。

因为是在类上调用,而非实例上调用,因此类方法无法获得任何实例变量,只能获得类的引用。

任务

如果将类属性count改为私有属性__count,则外部无法获取__count,但是可以通过一个类方法获取,请编写类方法获得__count值。

参考答案

注意类方法需要添加 @classmethod

class Animal(object):
    __localtion = 'Asia'
    __count = 0
    def __init__(self, name, age):
        self.name = name
        self.age = age
        Animal.__count += 1

    @classmethod
    def get_count(cls):
        return cls.__count

dog = Animal('wangwang', 1)
cat = Animal('mimi', 3)
pig = Animal('panpan', 1)
count = Animal.get_count()
print(count)

Python进阶教程笔记(二)类的继承、多态

2020-10-302020-10-30 11:42:23阅读 1660

一、什么是继承

  • 程序设计的范式
  • 抽象并建立对象模型
  • 程序是不同对象相互调用的逻辑

二、继承的优点

  • 新类不需要重头编写
  • 继承父类所有的属性、功能
  • 子类只需要实现缺少的新功能

三、继承类

对人类的抽象可以定义为Person类,而学生、老师等,也都是人类,所以,在Python当中,如果定义学生Student的类,可以继承Person类。

class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

接着定义Student类,在定义Student类的时候,由于继承了Person类,所以Student类自动拥有name、gender属性,因此,在定义Student类的时候,只需要把额外的属性加上即可。

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

student = Student('Alice', 'girl', 100)
print(student.name) # ==> Alice
print(student.gender) # ==> girl
print(student.score) # ==> 100

在定义继承类的时候,有几点是需要注意的:

  1. class Student()定义的时候,需要在括号内写明继承的类Person
  2. __init__()方法,需要调用super(Student, self).__init__(name, gender),来初始化从父类继承过来的属性

任务

请参考Student类,编写Teacher类,老师拥有任教某个科目的属性。

参考答案
class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

class Teacher(Person):
    def __init__(self, name, gender
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值