Python进阶

1. 类的定义

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

class Person:  pass

定义了类之后,就可以对类进行实例化了,实例化是指,把抽象的类,赋予实物的过程。比如,定义好Person这个类后,就可以实例化多个Person出来了。

Python实例属性的定义

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

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

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

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

定义类后,就可以相应的实例化对象了,需要注意的是,在实例化的时候,需要提供除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)

类和实例对象是有区别的,类是抽象,是模板,而实例则是根据类创建的对象,比如类:动物,只是一个抽象,并没有动物的详细信息,而猫、狗等,则是具体的动物,是类的对象。
在前面,实例对象绑定的属性只属于这个实例,绑定在一个实例上的属性不会影响其它实例;同样的,类也可以绑定属性,但是类的属性不属于任何一个对象,而是属于这个类。如果在类上绑定一个属性,则所有实例都可以访问类的属性,并且,所有实例访问的类属性都是同一个!也就是说,实例属性每个实例各自拥有,互相独立,而类属性有且只有一份。

class Animal(object):
    localtion = 'Asia'
    def __init__(self, name, age):
        self.name = name
        self.age = age
dog = Animal('wangwang', 1)
cat = Animal('mimi', 3)
print(dog.localtion) # ==> Asia
print(cat.localtion) # ==> Asia
# 类属性,也可以通过类名直接访问
print(Animal.localtion) # ==> Asia

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

# 类私有属性
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'

上一节课提到,私有属性没有办法从外部访问,只能在类的内部操作;那如果外部需要操作私有属性怎么办?这个时候可以通过定义类或者实例的方法来操作私有属性,本节课先来介绍实例方法。
实例的方法指的就是在类中定义的函数,实例方法的第一个参数永远都是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)方法,在外部就可以通过这个方法访问私有属性了。

注意,在外部调用实例方法时,是不需要显式传递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())

在上一节课,为了操作实例对象的私有属性,我们定义了实例方法;同样的,如果需要需要操作类的私有属性,则应该定义类的方法。
默认的,在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

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

类方法需要使用@classmethod来标记为类方法,否则定义的还是实例方法
类方法的第一个参数将传入类本身,通常将参数名命名为 cls,上面的 cls.__localtion 实际上相当于Animal.__localtion。
因为是在类上调用,而非实例上调用,因此类方法无法获得任何实例变量,只能获得类的引用。

2. 类的继承

对人类的抽象可以定义为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

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

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

随着我们学习步伐的前进,我们的程序会出现越来越多的类型,有我们自己定义的类,也有Python自有的str、list、dict等,他们的本质都是都是Python中的一种数据类型,这时有必要去判断数据的类型,通过函数isinstance()可以判断一个变量的类型。

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

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

class Teacher(Person):
    def __init__(self, name, gender, course):
        super(Teacher, self).__init__(name, gender)
        self.course = course

p = Person('Tim', 'Male')
s = Student('Bob', 'Male', 88)
t = Teacher('Alice', 'Female', 'English')

随着我们学习步伐的前进,我们的程序会出现越来越多的类型,有我们自己定义的类,也有Python自有的str、list、dict等,他们的本质都是都是Python中的一种数据类型,这时有必要去判断数据的类型,通过函数isinstance()可以判断一个变量的类型。

>>> isinstance(p, Person)
True # p是Person类型
>>> isinstance(p, Student)
False # p不是Student类型
>>> isinstance(p, Teacher)
False # p不是Teacher类型

3. 类的多态

类具有继承关系,并且子类类型可以向上转型看做父类类型,如果我们从 Person 派生出 Student和Teacher ,并都写了一个who() 方法:

class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender
    def who(self):
        return 'I am a Person, my name is %s' % self.name

class Student(Person):
    def __init__(self, name, gender, score):
        super(Student, self).__init__(name, gender)
        self.score = score
    def who(self):
        return 'I am a Student, my name is %s' % self.name

class Teacher(Person):
    def __init__(self, name, gender, course):
        super(Teacher, self).__init__(name, gender)
        self.course = course
    def who(self):
        return 'I am a Teacher, my name is %s' % self.name

接着,我们分别把不同类型的who()函数结果打印出来:

p = Person('Tim', 'Male')
s = Student('Bob', 'Male', 88)
t = Teacher('Alice', 'Female', 'English')
I am a Person, my name is Tim
I am a Student, my name is Bob
I am a Teacher, my name is Alice

这种行为称为多态。从定义上来讲,Student和Teacher都拥有来自父类Person继承的who()方法,以及自己定义的who()方法。但是在实际调用的时候,会首先查找自身的定义,如果自身有定义,则优先使用自己定义的函数;如果没有定义,则顺着继承链向上找。

4. 定义模块

Python语言本身提供了非常多的模块,比如数学模块math、cmath、decimal、statistics;文件模块pathlib、stat、shutil等;除了使用官方模块,有时候也需要自定义模块。
如果我们需要创建一个tools模块,用来实现众多的工具函数,那么我们可以创建一个tools.py的文件,并在这个文件里面实现一些函数,如:say_hello()函数、say_goodbye()函数。

tools.py

# tools.py
def say_hello():
    print('hello')

def say_goodbye():
    print('goodbye')

Python模块导入的路径
导入官方模块的时候,不需要考虑路径问题,这是因为在搜索模块的时候,会默认包含官方模块的路径,所以导入官方模块不需要考虑路径的问题。
如果需要导入自定义模块,则需要了解Python导入模块搜索的路径。
通过sys模块,可以知道导入模块的路径。

>>> import sys
>>> sys.path
['', '/data/miniconda3/lib/python3.8', '/data/miniconda3/lib/python3.8/site-packages']

它返回的是一个列表,表示的是在搜索Python模块时,会搜索的路径,在示例中,返回了四个路径。我们分析一些关键路径:
第一个路径是’',它是一个空字符串,表达的是当前路径的意思。
第二个路径是/data/miniconda3/lib/python3.8,它是Python默认模块的存放的路径,在这个路径下,可以发现有os、sys等模块的代码。
第三个路径是/data/miniconda3/lib/python3.8/site-packages,它是第三方模块代码的存放路径,在这个路径下,存放的是需要安装的第三方模块。

# main.py
import tools # 导入模块
tools.say_hello() # 调用模块里面的say_hello()函数
tools.say_goodbye() # 调用模块里面的say_goodbye()函数

就可以运行了。
因为在搜索包的路径时,会搜索当前路径(上述:sys.path结果的第一项),因此在同一个目录内的tools.py模块,可以被搜索到,所以能够import进来。

类的特殊方法

举例:str()
双下划线开头
双下划线结束
在这里插入图片描述

Python类的__str__ 和 __repr__方法

对于Python的内建对象,比如int、dict、list等,通过str()方法,可以把这些对象转换为字符串对象输出。

num = 12
str(num) # ==> '12'
d = {1: 1, 2: 2}
str(d) # ==> '{1: 1, 2: 2}'
l = [1,2,3,4,5]
str(l) # ==> '[1, 2, 3, 4, 5]'

Python类的__len__方法

对于列表List或者元组Tuple,通过内建方法len(),可以得出列表或者元组中元素的个数。如果一个类表现得像一个list,想使用len()函数来获取元素个数时,则需要实现__len__()方法。
比如我们实现一个班级Class的类,初始化把班级的同学名字列表传进去,希望len()函数可以返回班级同学的数量时,可以这样实现

class Class:
    def __init__(self, students):
        self.students = students
    def __len__(self):
        return len(self.students)
students = ['Alice', 'Bob', 'Candy']
class_ = Class(students)
len(class_) # ==> 3

Python类的__slots__方法

由于Python是动态语言,任何实例在运行期都可以动态地添加属性。比如:

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

此时,Student类有三个属性,name、gender、score,由于是动态语言,在运行时,可以随意添加属性。

student = Student('Bob', 'Male', 99)
student.age = 12 # ==> 动态添加年龄age属性

如果要限制添加的属性,例如,Student类只允许添加 name、gender和score 这3个属性,就可以利用Python的一个特殊的__slots__来实现。

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

使用__slots__ = (‘name’, ‘gender’, ‘score’) 限定Student类的属性,这个时候在外部再次添加动态属性age,将会报错。

>>> student = Student('Bob', 'Male', 99)
>>> student.age = 12 # ==> 动态添加年龄age属性
Traceback (most recent call last):
AttributeError: 'Student' object has no attribute 'age'

__slots__的目的是限制当前类所能拥有的属性,避免因为外部属性的操作导致类属性越来越难以管理。

Python类的__call__方法

在Python中,函数其实是一个对象,我们可以将一个函数赋值给一个变量,而不改变函数的功能。

>>> f = abs
>>> f
<built-in function abs>
>>> abs
<built-in function abs>
>>> f.__name__
'abs'
>>> f(-123)
123

把内建函数abs()赋值给变量f之后,可以看到f就和abs一样,都是。
由于 f 可以被调用,所以,f 被称为可调用对象,而事实上,所有的函数都是可调用对象。
如果把一个类实例也变成一个可调用对象,可以实现一个特殊的方法__call__()。
例如,我们把Person类变成一个可调用对象:

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

    def __call__(self, friend):
        print('My name is {}...'.format(self.name))
        print('My friend is {}...'.format(friend))
>>> p = Person('Bob', 'Male')
>>> p('Alice') # ==> 用函数的方式调用Person类的实例p
My name is Bob...
My friend is Alice...

Python函数式编程

Python的map()函数

map()是 Python 内置的高阶函数,它接收一个函数 f 和一个 list,并通过把函数 f 依次作用在list的每个元素上,map()函数会返回一个迭代器,可以依次迭代得到原来list的元素被函数f处理后的结果。

例如,对于list [1, 2, 3, 4, 5, 6, 7, 8, 9]。
如果希望把list的每个元素都作平方,就可以利用map()函数。

def f(x):
    return x*x
for item in map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9]):
    print(item)
   
[1, 4, 9, 10, 25, 36, 49, 64, 81]

Python的reduce()函数

和 map 函数一样,reduce() 函数也是 Python 内置的一个高阶函数。reduce() 函数接收的参数和 map() 类似,一个函数 f,一个list,但行为和 map() 不同,reduce() 传入的函数 f 必须接收两个参数,reduce() 对list 的每个元素反复调用函数 f,并返回最终结果值。
在python2中,reduce()函数和map()函数一样,可以直接使用,但是在python3中,reduce()函数被收录到functools包内,需要引入functools才可以使用。
例如,编写一个f函数,接收x和y,返回x和y的和:

Python的filter()函数

在这里插入图片描述

Python自定义排序函数

在这里插入图片描述

Python的闭包

在这里插入图片描述

Python的匿名函数

在这里插入图片描述

Python的偏函数

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python进阶之路》是一本非常值得推荐的Python进阶书籍。这本书由一位经验丰富的Python大牛所著,作者拥有超过20年的Python开发经验。这本书涵盖了许多Python进阶知识点,如元编程、动态属性、属性描述符、异步处理等。书中详细列举了这些高级特性的使用方法,并讲解得非常透彻。如果你想从入门迈向进阶,这本书是必备的参考资料。 另外,《Python Cookbook》也是一本非常受欢迎的Python进阶书籍。这本书总结了大量精妙的编程技巧和实用的技术,无论你是Python新手还是老手,都会从中收获很多。豆瓣评分高达9.2分,可见其受到广大读者的认可。 除了以上两本书,《Python进阶技巧》也是一本非常值得一读的进阶书籍。这本书的作者将许多代码简化成了一行,展现了Python的高级技巧。虽然有些地方可能看起来有些夸张,但它确实帮助你了解Python的特性和一些不错的功能。而且,在关键时刻,这种技巧还可以让你轻松搞定其他人需要十几行代码才能完成的任务。对于想要进阶的同学来说,这本书的阅读是非常适合的。 总而言之,《Python进阶之路》、《Python Cookbook》和《Python进阶技巧》都是非常优秀的Python进阶书籍,适合想要深入学习Python的读者。 : 引用自《Python进阶之路》 : 引用自《Python Cookbook》 : 引用自《Python进阶技巧》

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值