Python学习日记-2

115 阅读10分钟

循环代码优化

编写循环代码时, 遵守下面三个原则可以大大提高运行效率, 避免不必要的低效计算:

  • 尽量减少循环内部不必要的计算
  • 嵌套循环中, 尽量减少内层循环的计算, 尽可能向外提
  • 局部变量查询较快, 尽量使用局部变量

其他优化手段

  • 连接多个列表, 使用join()而不使用+
  • 列表进行元素插入和删除, 尽量在列表尾部操作
import time

start = time.time()
for i in range(1000):
    result = []
    for m in range(1000):
        c = i * 1000
        result = result + [m * 100]
end = time.time()
t = end - start
print(f"耗时{t}")

start2 = time.time()
for i in range(1000):
    result = []
    c = i * 1000
    for m in range(1000):
        result.append(c + m * 100)
end2 = time.time()
t2 = end2 - start2
print(f"耗时{t2}")

zip并行迭代多个序列

可以通过zip()函数对多个序列进行并行迭代, zip()函数在最短序列"用完"时就会停止

推导式创建序列

列表推导式

语法: [ 表达式 for item in 可迭代对象 ] 或者[ 表达式 for item in 可迭代对象 if 条件判断 ]

字典推导式

语法: { key : value for item in 可迭代对象 } 或者{ key : value for item in 可迭代对象 if 条件判断 }

集合推导式

语法: { 表达式 for item in 可迭代对象 } 或者{ 表达式 for item in 可迭代对象 if 条件判断 }

生成器推导式(不直接生成元组)

一个生成器只能运行一次, 第一次迭代可以得到数据, 第二次迭代数据, 数据就不存在了

# 文本计数器
my_text = "aabbcc"
char_count = {c: my_text.count(c) for c in my_text}
print(char_count)

面向对象

面向过程和面向对象的区别

面向过程和面向对象都是对软件分析, 设计和开发的一种思想, 它指导着人们以不同的方式去分析, 设计和开发软件, C语言是一种典型的面向过程语言, Java, C++都是一种典型的面向对象语言

面向对象离不开面向过程:

宏观上: 通过面向对象进行整体设计

微观上: 执行和处理数据, 仍然是面向过程

__init__()构造方法和__new__()方法

初始化对象, 需要定义构造方法__init__()方法, 构造方法用于执行"实例对象的初始化工作", 即对象创建后, 初始化当前对象的相关属性, 无返回值

__init__()方法: 初始化对象, 初始化是指"给实例属性赋值"

__new__()方法: 用于创建对象, 但一般无需重新定义方法

如果我们不定义__init__()方法, 系统会提供一个默认无参的__init__()方法, 如果我们定义了带参数的__init__()方法, 系统不创建默认的__init__()方法

Python中的self相当于C++的self指针, Java和C#的this关键字

Python中, self必须为构造函数的第一个参数,名字可以任意修改, 但一般惯例, 默认叫self

实例属性和实例方法

实例方法_内存分析

函数和方法的区别

  • 都是用来完成一个功能的语句块, 本质一样
  • 方法调用时, 通过对象来调用, 方法从属于特定实例对象, 普通函数没有这个特点
  • 直观上看, 方法定义时需要传递self, 函数不需要

实例对象的方法调用本质

a = Student()
				>>>>>>>>>>	Student.say_score(a, 100)
a.say_score(100)

其他操作

  • dir(obj) : 可以获得对象的所有属性和方法
  • obj.__dict__对象的属性字典
  • pass : 空语句
  • isinstance(对象, 类型) : 判断对象是不是指定类型

类对象/类属性/类方法/静态方法

类对象

类定义格式: class 类名:

当解释器执行class语句时, 就会创建一个类对象

类属性

类属性时从属于"类对象"的属性, 也称为"类变量", 由于, 类属性从属于类对象, 可以被所有实例对象共享

类属性的定义方式:

class 类名:
    类变量名 = 初始值

在类中或者类的外面, 可以通过: 类名.类变量名 来读写

内存分析实例对象和类对象创建过程

![输入图片说明](R(4$FOVHXNIJFZ1FVJ%7DOW6D.png)

类方法和静态方法

类方法是从属于"类对象"的方法, 类方法通过装饰器@classmethod来定义, 格式如下:

@classmethod
def 类方法名(cls, [形参列表]):
    方法体    

要点如下 :

  • @classmethod必须位于方法上面一行
  • 第一个参数cls必须有, cls指的是类对象本身
  • 调用类方法格式: 类名.类方法名(参数列表), 参数列表中, 不需要也不能给cls传值
  • 类方法中访问实例属性和实例方法会导致错误(ps: 父级类模板不可能往下子级调用的)
  • 子类继承父类方法时, 传入cls是子类对象, 而非父类对象

静态方法

Python中允许定义与"类对象"无关的方法, 称为"静态方法"

"静态方法"和模块中定义普通函数没有区别, 只不过"静态方法"放到类对象里面, 需要通过类调用

静态方法通过装饰器@staticmethod来定义, 格式如下:

@staticmethod
def 静态方法名([形参列表]):
	方法体

要点如下:

  • @staticmethod必须位于方法上面一行
  • 调用静态方法格式: 类名.静态方法名(参数列表)
  • 静态方法中访问实例属性和实例方法会导致错误
class Student:
    a = 100
    b = 300

    def __init__(self):
        pass

    def cc(self):
        pass

    @classmethod
    def dd(cls, aa):
        print(aa)
        print(cls.a)

    @staticmethod
    def ee(aa):
        print(aa)


print(dir(Student))
print(id(Student), type(Student))

__del__析构方法和垃圾回收机制

__del__()称为析构方法, 用于实现对象被销毁时所需的操作, 比如: 释放对象占用的资源(打开的文件资源, 网络连接等)

Python实现自动的垃圾回收, 当对象没有被引用时(引用计数为0), 由垃圾回收器调用__del__()

我们也可以通过del语句手动删除对象, 从而保证调用__del__(), 比如: del obj

系统会自动提供__del__()方法, 一般不需要自定义析构方法

__call__方法和可调用对象

  • Python中, 凡是可以将()直接应用到自身并执行, 都称为可调用对象
  • 可调用对象包括自定义的函数, Python内置函数, 以及实例对象
  • 定义了__call__()的对象, 称为"可调用对象", 即对象可以像函数一样被调用
  • 该方法使得实例对象可以像调用普通函数那样, 以"对象名()"的形式使用
def func1():
    print("function")


print(id(func1))
print(type(func1))


class Demo:
    def __init__(self):
        print("调用了__init__方法")

    def __call__(self, *args, **kwargs):
        print("调用了__call__方法")


aa = Demo()		# 创建实例对象时, 是调用了构造函数, 没有调用call函数
aa()

Python方法无重载 和 方法动态性

方法无重载

如果我们在类里面定义多个重名的方法, 只有最后一个方法为有效的

提示: 不要使用重名的方法, Python中方法没有重载

  • 在其他语言(比如Java)中, 可以定义多个重名的方法, 只要保证方法签名唯一即可, 方法签名包含方法名, 参数数量, 参数类型
  • Python中, 方法的参数没有声明类型(调用时确认参数的类型), 参数的数量也是可以由可变参数控制, 因此, Python中是没有方法的重载的

方法的动态性

Python是动态语言, 我们可以动态的为类添加新的方法, 或者动态的修改类的已有方法

代码演示:

class Demo:
    def aa(self):
        pass

    def aa(self, item):
        print(item)


obj1 = Demo()
obj1.aa(100)  # 方法无重载


def aa(self):  # 动态修改已有的方法, 参数至少加一个(创建对象self默认参数)
    print("动态修改已有的方法")


def bb(self):
    print("动态添加新的方法")


Demo.aa = aa
Demo.bbb = bb

obj2 = Demo()
obj2.aa()
obj2.bbb()

私有属性和私有方法(实现封装)

私有属性

Python对于类的成员没有严格的访问控制限制, 这与其他面向对象语言有区别

关于私有属性和私有方法, 有如下要点:

  • 通常我们约定, 两个下划线开头的属性是私有的(private), 其他为公开的(public)
  • 类内部可以访问私有属性(方法)
  • 类外部不能直接访问私有属性(方法)
  • 类外部可以通过XXX._类名__私有属性(方法)名, 来访问私有属性(方法)

私有方法

注意: 方法本质上也是属性, 只不过可以通过()执行

演示代码:

class Demo:
    __item1 = "11"		# 解释器运行时, 会把__item1自动转换为_Demo__item1

    def __init__(self, item2):
        self.__item2 = item2

    def method1(self):
        print(self.__item1, self.__item2)


print(dir(Demo))
print(Demo._Demo__item1)
aa = Demo(66)
print(aa._Demo__item1)
print(aa._Demo__item2)
aa.method1()

@property装饰器

@property可以将一个方法的调用方式变成"属性调用"

@property主要用于帮助我们处理属性的读/写操作

对于某个属性, 我们可以直接通过: odj.salary = 3000

如上的操作读写操作做法不安全, 比如说我需要限制薪水为1-10000之间的数字, 这时候我们就需要用到装饰器@property来处理

装饰器本质上是一个 Python 函数或类,它可以让其他函数或类在不需要做任何代码修改的前提下增加额外功能,装饰器的返回值也是一个函数/类对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景,装饰器是解决这类问题的绝佳设计。有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码到装饰器中并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。

代码演示

class Demo:
    def __init__(self, name, age, salary):
        self.name = name
        self.age = age
        self.__salary = salary

    @property
    def salary(self):
        print("您的月薪为{}".format(self.__salary))
        return self.__salary

    @salary.setter
    def salary(self, salary):
        if 0 < salary < 10000:
            self.__salary = salary
        else:
            print("薪资录入有误!只能在0-10000之间!")


p = Demo("jack", 18, 3000)
p.salary
p.salary = 8000
p.salary = 100000
# p.salary

属性和方法命名总结

  • _xxx: 保护成员, 不能用from module import * 导入, 只有类对象和子类对象能访问这些成员

  • __xxx__: 系统定义的特殊成员, 比如__init__

  • __xxx: 类中的私有成员, 只有类对象自己能访问, 子类对象也不能访问(但在类外部可以通过对象名._类名__xxx这种特殊方式访问, Python不存在严格意义上的私有成员)

    注意: 方法和属性都遵循上面的规则

None对象的特殊性

None是什么

  • 与C和Java不同, Python中是没有NULL的, 取而代之的是None
  • None是一种特殊的常量, 表示变量没有指向任何对象
  • 在Python中, None本身实际上也是对象, 有自己的类型NoneType
  • 你可以将None赋值给任何变量, 但不能创建NoneType类型的对象

注意: None不是False, None不是0, None不是空字符串, None和任何其他的数据类型比较永远返回False

空列表/空字符串/0之间的比较

  • if语句判断时, 空列表[], 空字典{}, 空元组(), 0等一系列代表空和无的对象会被转换为False
  • ==和is判断时, 空列表[], 空字符串不会自动转成False