面向对象 元类 gil log 协程 垃圾回收 描述符 property

 封装、继承、多态

当谈到封装、继承和多态时,通常是在面向对象编程 (OOP) 的上下文中讨论的。

封装 (Encapsulation) 示例:

class Person:
    def __init__(self, name, age):
        self.__name = name  # 使用双下划线前缀将属性变为私有
        self.__age = age
​
    def get_name(self):
        return self.__name
​
    def set_name(self, new_name):
        if len(new_name) > 0:
            self.__name = new_name
​
    def get_age(self):
        return self.__age
​
    def set_age(self, new_age):
        if new_age >= 0:
            self.__age = new_age
​
# 创建一个Person对象并访问属性
person = Person("Alice", 30)
print(person.get_name())  # 输出:Alice
person.set_age(32)  # 设置年龄
print(person.get_age())  # 输出:32

在这个示例中,我们通过将属性标记为私有(使用双下划线前缀)来封装数据,并提供公共方法(get_nameset_nameget_ageset_age)来访问和修改这些属性。这样,我们可以控制属性的访问和修改,以确保数据的安全性。

使用面向对象来开发,示例代码如下:

  • 类:理解为模板
  • 对象:理解为实物
  1. 在使用面向过程编程时,当需要对数据处理时,需要考虑用哪个模板中哪个函数来进行操作,但是当用面向对象编程时,因为已经将数据存储到了这个独立的空间中,这个独立的空间(即对象)中通过一个特殊的变量(__class__)能够获取到类(模板),而且这个类中的方法是有一定数量的,与此类无关的将不会出现在本类中,因此需要对数据处理时,可以很快速的定位到需要的方法是谁 这样更方便
  2. 全局变量是只能有1份的,多个函数需要多个备份时,往往需要利用其它的变量来进行储存;而通过封装 会将用来存储数据的这个变量 变为了对象中的一个“全局”变量,只要对象不一样那么这个变量就可以再有1份,所以这样更方便
  3. 代码划分更清晰

继承 (Inheritance) 示例:

class Animal:
    def __init__(self, name):
        self.name = name
​
    def speak(self):
        pass
​
class Dog(Animal):
    def speak(self):
        return f"{self.name} 说:汪汪汪!"
​
class Cat(Animal):
    def speak(self):
        return f"{self.name} 说:喵喵喵!"
​
# 创建一个Dog对象和一个Cat对象,并调用speak方法
dog = Dog("旺财")
cat = Cat("小花")
print(dog.speak())  # 输出:旺财 说:汪汪汪!
print(cat.speak())  # 输出:小花 说:喵喵喵!

在这个示例中,我们定义了一个基类 Animal,然后创建了两个派生类 DogCat,它们继承了 Animal 的属性和方法。每个派生类都可以覆盖基类的方法(例如 speak),以实现自己的行为。

  1. 能够提升代码的重用率,即开发一个类,可以在多个子功能中直接使用
  2. 继承能够有效的进行代码的管理,当某个类有问题只要修改这个类就行,而其继承这个类的子类往往不需要就修改

多态 (Polymorphism) 示例:

class Shape:
    def area(self):
        pass
​
class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius
​
    def area(self):
        return 3.14 * self.radius * self.radius
​
class Rectangle(Shape):
    def __init__(self, length, width):
        self.length = length
        self.width = width
​
    def area(self):
        return self.length * self.width
​
# 创建一个Shape的列表,包含不同类型的图形对象
shapes = [Circle(5), Rectangle(4, 6), Circle(3)]
​
# 计算并输出每个图形的面积
for shape in shapes:
    print(f"面积:{shape.area()}")

在这个示例中,我们定义了一个基类 Shape,以及两个派生类 CircleRectangle,它们都具有一个名为 area 的方法。通过多态,我们可以在一个列表中存储不同类型的图形对象,然后通过统一的方法调用来计算它们的面积。这就是多态的核心思想,不同的对象可以对相同的方法做出不同的响应。

class MiniOS(object):
    """MiniOS 操作系统类 """
    def __init__(self, name):
        self.name = name
        self.apps = []  # 安装的应用程序名称列表

    def __str__(self):
        return "%s 安装的软件列表为 %s" % (self.name, str(self.apps))

    def install_app(self, app):
        # 判断是否已经安装了软件
        if app.name in self.apps:
            print("已经安装了 %s,无需再次安装" % app.name)
        else:
            app.install()
            self.apps.append(app.name)


class App(object):
    def __init__(self, name, version, desc):
        self.name = name
        self.version = version
        self.desc = desc

    def __str__(self):
        return "%s 的当前版本是 %s - %s" % (self.name, self.version, self.desc)

    def install(self):
        print("将 %s [%s] 的执行程序复制到程序目录..." % (self.name, self.version))


class PyCharm(App):
    pass


class Chrome(App):
    def install(self):
        print("正在解压缩安装程序...")
        super().install()


linux = MiniOS("Linux")
print(linux)

pycharm = PyCharm("PyCharm", "1.0", "python 开发的 IDE 环境")
chrome = Chrome("Chrome", "2.0", "谷歌浏览器")
chrome2 = Chrome("Chrome2", "3.0", "古哥浏览器")

linux.install_app(pycharm)
linux.install_app(chrome)
linux.install_app(chrome)
linux.install_app(chrome2)

print(linux)

Linux 安装的软件列表为 []
将 PyCharm [1.0] 的执行程序复制到程序目录...
正在解压缩安装程序...
将 Chrome [2.0] 的执行程序复制到程序目录...
已经安装了 Chrome,无需再次安装
正在解压缩安装程序...
将 Chrome2 [3.0] 的执行程序复制到程序目录...
Linux 安装的软件列表为 ['PyCharm', 'Chrome', 'Chrome2']

多态需要用到继承,重写,调用某个方法时,要看是父类创建的实例,还是子类创建的实例,实例不同调用的方法不同

关键点:

  1. 定义了一个基类App,它具有name、version、desc属性,以及一个install方法用于安装。
  2. 定义了两个子类PyCharm和Chrome,继承自App。
  3. Chrome类对install方法进行了重写,添加了自定义的安装逻辑。
  4. 在MainOS类中,通过install_app方法安装不同的App实例对象,调用其install方法。
  5. 对于同一个install_app方法,传入不同的类的实例,会调用各自类中不同的install实现。这就是多态。
  6. 多态允许我们基于同一接口编写可扩展和可维护的代码。子类可以改变父类的行为,同时保持接口一致。

所以多态的关键是:

  • 有继承关系
  • 子类覆盖父类方法
  • 对不同子类实例调用相同的方法

这使得调用者可以一致地调用方法,而具体执行代码依赖于运行时对象的类型。这就是多态

  • 面向过程开发,简单、开发前期快速,越往后越复杂,适合小工程

  • 面向对象开发,复杂、开发前期较慢,越往后开发越方便,适合大工程

    没有最好的开发模式,只有经过多多练习,见的多了,感受多了,自然也就能够在不同的任务、不同的工程,使用合适的方式进行开发

静态方法和类方法

类属性、实例属性

它们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同,

  • 实例属性属于对象

  • 类属性属于类

class Province(object):
    # 类属性
    country = 'china'

    def __init__(self, name):
        # 实例属性
        self.name = name


# 创建一个实例对象
obj = Province('shangdong')
# 直接访问实例属性
print(obj.name)
# 直接访问类属性
Province.country

由上述代码可以看出【实例属性需要通过对象来访问】【类属性通过类访问】,在使用上可以看出实例属性和类属性的归属是不同的。

其在内容的存储方式类似如下图:

由上图看出:

  • 类属性在内存中只保存一份

  • 实例属性在每个对象中都要保存一份

  • 通过类创建实例对象时,如果每个对象需要具有相同名字的属性,那么就使用类属性,用一份既可

实例方法、静态方法和类方法

方法包括:实例方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同。

  • 实例方法:由对象调用;至少一个self参数;执行实例方法时,自动将调用该方法的对象赋值给self;

  • 类方法:由类调用; 至少一个cls参数;执行类方法时,自动将调用该方法的类赋值给cls;

  • 静态方法:由类调用;无默认参数;

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

    def ord_func(self):
        """ 定义实例方法,至少有一个self参数 """
        # print(self.name)
        print('实例方法')

    @classmethod
    def class_func(cls):
        """ 定义类方法,至少有一个cls参数 """
        print('类方法')

    @staticmethod
    def static_func():
        """ 定义静态方法 ,无默认参数"""
        print('静态方法')


f = Foo("china")
# 调用实例方法
f.ord_func()
# 调用类方法
Foo.class_func()
# 调用静态方法
Foo.static_func()
实例方法
类方法
静态方法
  • 相同点:对于所有的方法而言,均属于类,所以 在内存中也只保存一份
  • 不同点:方法调用者不同、调用方法时自动传入的参数不同。
# 实例方法
# self参数表示当前对象的实例
class Person:
    def __init__(self, name):
        self.name = name

    def say_hello(self):
        print(f'Hello, my name is {self.name}')

p = Person('John')
p.say_hello() # 调用实例方法,自动传递当前实例p作为self参数

# 静态方法
# 使用@staticmethod装饰器声明,不需要默认的self参数
class Person:
    def __init__(self, name):
        self.name = name

    @staticmethod
    def hello():
        print('Hello!')

Person.hello() # 直接使用类名调用静态方法,不需要实例化

# 类方法
# 使用@classmethod装饰器声明,默认参数cls代表当前类本身
class Person:
    count = 0 # 类属性

    def __init__(self, name):
        self.name = name
        Person.count += 1

    @classmethod
    def print_count(cls):

        print(f'Number of people: {cls.count}')

Person.print_count() # 使用类名调用,将类本身作为参数传递
person=Person('')
person.print_count()

Hello, my name is John
Hello!
Number of people: 0
Number of people: 1


# 实例方法需要实例化对象后才能调用,自动传递self参数
# 静态方法不需要实例化,通过类名直接调用
# 类方法也不需要实例化,会将类本身作为第一个参数传递

多继承以及MRO顺序

1. 多继承中调用父类方式不同结果不同

2.1 单独调用父类的方法

# coding=utf-8
​
print("******多继承使用类名.__init__ 发生的状态******")
class Parent(object):
    def __init__(self, name):
        print('parent的init开始被调用')
        self.name = name
        print('parent的init结束被调用')
​
class Son1(Parent):
    def __init__(self, name, age):
        print('Son1的init开始被调用')
        self.age = age
        Parent.__init__(self, name)
        print('Son1的init结束被调用')
​
class Son2(Parent):
    def __init__(self, name, gender):
        print('Son2的init开始被调用')
        self.gender = gender
        Parent.__init__(self, name)
        print('Son2的init结束被调用')
​
class Grandson(Son1, Son2):
    def __init__(self, name, age, gender):
        print('Grandson的init开始被调用')
        Son1.__init__(self, name, age)  # 单独调用父类的初始化方法
        Son2.__init__(self, name, gender)
        print('Grandson的init结束被调用')
​
gs = Grandson('grandson', 12, '男')
print('姓名:', gs.name)
print('年龄:', gs.age)
print('性别:', gs.gender)
​
print("******多继承使用类名.__init__ 发生的状态******\n\n")
复制Error复制成功...
运行结果:

******多继承使用类名.__init__ 发生的状态******
Grandson的init开始被调用
Son1的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son1的init结束被调用
Son2的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son2的init结束被调用
Grandson的init结束被调用
姓名: grandson
年龄: 12
性别: 男
******多继承使用类名.__init__ 发生的状态******复制Error复制成功...

2.2 多继承中super调用被重写的父类方法

print("******多继承使用super().__init__ 发生的状态******")
class Parent(object):
    def __init__(self, name, *args, **kwargs):  # 为避免多继承报错,使用不定长参数,接受参数
        print('parent的init开始被调用')
        self.name = name
        print('parent的init结束被调用')
​
class Son1(Parent):
    def __init__(self, name, age, *args, **kwargs):  # 为避免多继承报错,使用不定长参数,接受参数
        print('Son1的init开始被调用')
        self.age = age
        super().__init__(name, *args, **kwargs)  # 为避免多继承报错,使用不定长参数,接受参数
        print('Son1的init结束被调用')
​
class Son2(Parent):
    def __init__(self, name, gender, *args, **kwargs):  # 为避免多继承报错,使用不定长参数,接受参数
        print('Son2的init开始被调用')
        self.gender = gender
        super().__init__(name, *args, **kwargs)  # 为避免多继承报错,使用不定长参数,接受参数
        print('Son2的init结束被调用')
​
class Grandson(Son1, Son2):
    def __init__(self, name, age, gender):
        print('Grandson的init开始被调用')
        # 多继承时,相对于使用类名.__init__方法,要把每个父类全部写一遍
        # 而super只用一句话,执行了全部父类的方法,这也是为何多继承需要全部传参的一个原因
        # super(Grandson, self).__init__(name, age, gender)
        super().__init__(name, age, gender)
        print('Grandson的init结束被调用')
​
print(Grandson.__mro__)
​
gs = Grandson('grandson', 12, '男')
print('姓名:', gs.name)
print('年龄:', gs.age)
print('性别:', gs.gender)
print("******多继承使用super().__init__ 发生的状态******\n\n")复制Error复制成功...
运行结果:

******多继承使用super().__init__ 发生的状态******
(, , , , )
Grandson的init开始被调用
Son1的init开始被调用
Son2的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son2的init结束被调用
Son1的init结束被调用
Grandson的init结束被调用
姓名: grandson
年龄: 12
性别: 男
******多继承使用super().__init__ 发生的状态******复制Error复制成功...

2.3 注意

以上2个代码执行的结果不同

  1. 如果2个子类中都继承了父类,当在子类中通过父类名调用时,parent被执行了2次

  2. 如果2个子类中都继承了父类,当在子类中通过super调用时,parent被执行了1次

2. 单继承中super

print("******单继承使用super().__init__ 发生的状态******")
class Parent(object):
    def __init__(self, name):
        print('parent的init开始被调用')
        self.name = name
        print('parent的init结束被调用')
    def p(self): #继承父类的方法
        print('父类')

class Son1(Parent):
    def __init__(self, name, age):
        print('Son1的init开始被调用')
        self.age = age
        super().__init__(name)  # 单继承不能提供全部参数
        print('Son1的init结束被调用')

class Grandson(Son1):
    def __init__(self, name, age, gender):
        print('Grandson的init开始被调用')
        super().__init__(name, age)  # 单继承不能提供全部参数
        print('Grandson的init结束被调用')

gs = Grandson('grandson', 12, '男')
print('姓名:', gs.name)
print('年龄:', gs.age)
print('年龄:', gs.p())
# print('性别:', gs.gender)
print("******单继承使用super().__init__ 发生的状态******\n\n")

******单继承使用super().__init__ 发生的状态******
Grandson的init开始被调用
Son1的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son1的init结束被调用
Grandson的init结束被调用
姓名: grandson
年龄: 12
父类
年龄: None
******单继承使用super().__init__ 发生的状态******


总结

  1. super().__init__相对于类名.__init__,在单继承上用法基本无差

  2. 但在多继承上有区别,super方法能保证每个父类的方法只会执行一次,而使用类名的方法会导致方法被执行多次,具体看前面的输出结果

  3. 多继承时,使用super方法,对父类的传参数,由于super的算法导致的原因,必须把参数全部传递,否则会报错

  4. 单继承时,使用super方法,则不能全部传递,只能传父类方法所需的参数,否则会报错

  5. 多继承时,相对于使用类名.init方法,要把每个父类全部写一遍, 而使用super方法,只需写一句话便执行了全部父类的方法,这也是为何多继承需要全部传参的一个原因

小试牛刀(以下为面试题)

以下的代码的输出将是什么? 说出你的答案并解释。

class Parent(object):
    x = 1
 
class Child1(Parent):
    pass
 
class Child2(Parent):
    pass
 
print(Parent.x, Child1.x, Child2.x)
Child1.x = 2
print(Parent.x, Child1.x, Child2.x)
Parent.x = 3
print(Parent.x, Child1.x, Child2.x)
复制Error复制成功...
答案, 以上代码的输出是:

1 1 1
1 2 1
3 2 3

使你困惑或是惊奇的是关于最后一行的输出是 3 2 3 而不是 3 2 1。为什么改变了 Parent.x 的值还会改变 Child2.x 的值,但是同时 Child1.x 值却没有改变?

这个答案的关键是,在 Python 中,类变量在内部是作为字典处理的。如果一个变量的名字没有在当前类的字典中发现,将搜索祖先类(比如父类)直到被引用的变量名被找到(如果这个被引用的变量名既没有在自己所在的类又没有在祖先类中找到,会引发一个 AttributeError 异常 )。

因此,在父类中设置 x = 1 会使得类变量 x 在引用该类和其任何子类中的值为 1。这就是因为第一个 print 语句的输出是 1 1 1。

随后,如果任何它的子类重写了该值(例如,我们执行语句 Child1.x = 2),然后,该值仅仅在子类中被改变。这就是为什么第二个 print 语句的输出是 1 2 1。

最后,如果该值在父类中被改变(例如,我们执行语句 Parent.x = 3),这个改变会影响到任何未重写该值的子类当中的值(在这个示例中被影响的子类是 Child2)。这就是为什么第三个 print 输出是 3 2 3。

内建属性

1. 内建属性

内建属性,往往是指我们,在使用类时,可以直接使用的那些功能,例如__new____init__

1.1 新式类

Python3中定义的类都是新式类的,无论是否写明一个类继承object,都会间接或直接继承object

teachclass.py
class Person(object):
    pass

Python3中类的内建属性和方法

常用内建属性

常用专有属性 说明 触发方式
__init__ 构造初始化函数 创建实例后,赋值时使用,在__new__后自动调用
__new__ 生成实例所需属性 创建实例时
__class__ 实例所在的类 实例.__class__
__str__ 实例字符串表示,可读性 print(类实例)如没实现,使用repr结果
__repr__ 实例字符串表示,准确性 类实例 回车 或者 print(repr(类实例))
__del__ 析构(对象被删除前做清理工作) del 实例后,如果对象引用计数为0,则自动调用
__dict__ 实例自定义属性 vars(实例.__dict__)
__doc__ 类文档,子类不继承 help(类或实例)
__getattribute__ 属性访问拦截器 访问实例属性时
__bases__ 类的所有父类构成元素 类名.__bases__
# 示例和解释Python特殊方法和属性

# __init__: 用于初始化对象的构造器方法。
# 参数 self: 表示实例本身。
# 参数 args: 表示传递给构造器的参数,可变数量。
class ExampleClass:
    def __init__(self, *args):
        self.args = args

# __new__: 用于创建并返回类的新实例,通常用于元类或控制实例创建的高级用途。
# 参数 cls: 表示类本身。
# 参数 args: 表示传递给构造器的参数,可变数量。
class ExampleClassNew:
    def __new__(cls, *args):
        instance = super(ExampleClassNew, cls).__new__(cls)
        return instance

# __class__: 特殊属性,用于获取对象所属的类。
example = ExampleClass()
example_class = example.__class__
print('example_class',example_class)
# __str__: 定义对象的“非正式”或可打印字符串表示。
# 参数 self: 表示实例本身。
class ExampleStr:
    def __str__(self):
        return "Example of __str__"
e=ExampleStr()
print('e.__str__()',e.__str__())
# __repr__: 定义对象的“官方”字符串表示,通常用于调试。
# 参数 self: 表示实例本身。
class ExampleRepr:
    def __repr__(self):
        return "Example of __repr__"

# __del__: 析构器方法,在对象被销毁前调用。
# 参数 self: 表示实例本身。
class ExampleDel:
    def __del__(self):
        print("Example of __del__")

# __dict__: 特殊属性,包含了对象的属性字典。
example_dict = example.__dict__
print('example_dict',example_dict)
# __doc__: 特殊属性,包含了类的文档字符串。
example_doc = ExampleClass.__doc__
print('example_doc',example_doc)

# __getattribute__: 用于获取属性的特殊方法。
# 参数 self: 表示实例本身。
# 参数 name: 属性名称。
class ExampleGetattribute:
    def __getattribute__(self, name):
        return f"Attribute {name} accessed"

# __bases__: 特殊属性,包含了类的基类元组。
example_bases = ExampleClass.__bases__
print('example_bases',example_bases)

下面是一些Python特殊方法和属性的例子,以及每个方法和属性的中文解释和参数意义:

1. `__init__`: 这是一个构造器方法,用于初始化对象。
   - `self`: 表示实例本身。
   - `*args`: 表示传递给构造器的参数,数量可变。

2. `__new__`: 用于创建并返回类的新实例,通常用于元类或控制实例创建的高级用途。
   - `cls`: 表示类本身。
   - `*args`: 表示传递给构造器的参数,数量可变。

3. `__class__`: 这是一个特殊属性,用于获取对象所属的类。

4. `__str__`: 定义对象的“非正式”或可打印字符串表示。
   - `self`: 表示实例本身。

5. `__repr__`: 定义对象的“官方”字符串表示,通常用于调试。
   - `self`: 表示实例本身。

6. `__del__`: 这是一个析构器方法,在对象被销毁前调用。
   - `self`: 表示实例本身。

7. `__dict__`: 这是一个特殊属性,包含了对象的属性字典。

8. `__doc__`: 这是一个特殊属性,包含了类的文档字符串。

9. `__getattribute__`: 用于获取属性的特殊方法。
   - `self`: 表示实例本身。
   - `name`: 属性名称。

10. `__bases__`: 这是一个特殊属性,包含了类的基类元组。

3. __getattribute__属性

__getattribute__功能很强大:能够完成属性访问时进行拦截

例子:

class Itcast(object):
    def __init__(self,subject1):
        self.subject1 = subject1
        self.subject2 = 'cpp'
​
    # 属性访问时拦截器,打log
    def __getattribute__(self,obj):
        if obj == 'subject1':
            print('log subject1')
            return 'redirect python'
        else:  # 测试时注释掉这2行,将找不到subject2
            return object.__getattribute__(self,obj)
​
    def show(self):
        print('this is Itcast')
​
        
s = Itcast("python")
print(s.subject1)
print(s.subject2)
运行结果:

log subject1
redirect python
cpp

getattribute的坑

    class Person(object):
        def __getattribute__(self,obj):
            print("---test---")
            if obj.startswith("a"):
                return "hahha"
            else:
                return self.test


        def test(self):
            print("heihei")


    t.Person()

    t.a  # 返回hahha

    t.b  
# 会让程序死掉
# 原因是:当t.b执行时,会调用Person类中定义的__getattribute__方法,但是在这个方法的执行过程中
# if条件不满足,所以 程序执行else里面的代码,即return self.test  问题就在这,因为return 需要把
# self.test的值返回,那么首先要获取self.test的值,因为self此时就是t这个对象,所以self.test就是
# t.test 此时要获取t这个对象的test属性,那么就会跳转到__getattribute__方法去执行,即此时产
# 生了递归调用,由于这个递归过程中 没有判断什么时候推出,所以这个程序会永无休止的运行下去,又因为
# 每次调用函数,就需要保存一些数据,那么随着调用的次数越来越多,最终内存吃光,所以程序 崩溃
#
# 注意:以后不要在__getattribute__方法中调用self.xxxx

魔法属性

无论人或事物往往都有不按套路出牌的情况,Python的类属性也是如此,存在着一些具有特殊含义的属性,详情如下:

1. doc

  • 表示类的描述信息

class Foo:
    """ 描述类信息,这是用于看片的神奇 """
    def func(self):
        pass
​
print(Foo.__doc__)
#输出:类的描述信息
​
描述类信息,这是用于看片的神奇

2. moduleclass

  • module 表示当前操作的对象在那个模块

  • class 表示当前操作的对象的类是什么

test.py
class Person(object):
    def __init__(self):
        self.name = 'laowang'
​
main.py
from test import Person
​
obj = Person()
print(obj.__module__)  # 输出 test 即:输出模块
print(obj.__class__)  # 输出 test.Person 即:输出类

3. init

  • 初始化方法,通过类创建对象时,自动触发执行

class Person:
    def __init__(self, name):
        self.name = name
        self.age = 18
​
​
obj = Person('laowang')  # 自动执行类中的 __init__ 方法

4. del

  • 当对象在内存中被释放之前,自动触发执行

注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,del的调用是由解释器在进行垃圾回收前 自动触发,执行一些完善工作

深度学习除外

class Foo:
    def __del__(self):
        pass

5. call

  • 对象后面加括号,触发执行。

注:init方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 call 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()

class Foo:
    def __init__(self):
        pass
    
    def __call__(self, *args, **kwargs):
        print('__call__')
​
​
obj = Foo()  # 执行 __init__
obj()  # 执行 __call__

6. dict

  • 类或对象中的所有属性

类的实例属性属于对象;类中的类属性和方法等属于类,即:

class Province(object):
    country = 'China'
​
    def __init__(self, name, count):
        self.name = name
        self.count = count
​
    def func(self, *args, **kwargs):
        print('func')
​
# 获取类的属性,即:类属性、方法、
print(Province.__dict__)
# 输出:{'__dict__': , '__module__': '__main__', 'country': 'China', '__doc__': None, '__weakref__': , 'func': , '__init__': }
​
obj1 = Province('山东', 10000)
print(obj1.__dict__)
# 获取 对象obj1 的属性
# 输出:{'count': 10000, 'name': '山东'}
​
obj2 = Province('山西', 20000)
print(obj2.__dict__)
# 获取 对象obj1 的属性
# 输出:{'count': 20000, 'name': '山西'}

7. str

  • 如果一个类中定义了str方法,那么在打印 对象 时,默认输出该方法的返回值。

class Foo:
    def __str__(self):
        return 'laowang'
​
​
obj = Foo()
print(obj)
# 输出:laowang

8、getitemsetitemdelitem

  • 用于索引操作,如字典。以上分别表示获取、设置、删除数据

 
  
# -*- coding:utf-8 -*-
 
class Foo(object):
 
    def __getitem__(self, key):
        print('__getitem__', key)
 
    def __setitem__(self, key, value):
        print('__setitem__', key, value)
 
    def __delitem__(self, key):
        print('__delitem__', key)
 
 
obj = Foo()
 
result = obj'k1']      # 自动触发执行 __getitem__
obj['k2'] = 'laowang'   # 自动触发执行 __setitem__
del obj'k1']           # 自动触发执行 __delitem__

内建函数

1.内建函数

1.1什么是内建函数

Build-in Function,启动python解释器,输入dir(__builtin__), 可以看到很多python解释器启动后默认加载的属性和函数,这些函数称之为内建函数

通俗的说:

内建函数通常值得是,我们可以直接使用那些函数,而不是指在类中方法,例如helplenprint

1.2注意

内建函数因为在编程时使用较多,cpython解释器c语言实现了这些函数,启动解释器 时默认加载。

这些函数数量众多,不宜记忆,开发时不是都用到的,待用到时再help(function), 查看如何使用,或结合谷歌查询即可,在这里介绍些常用的内建函数。

2.常用内建函数案例

2.1range函数

Python2range返回列表,Python3range返回一个range对象,是一个可以迭代的对象,可以配合for或者next等使用

当然了如果想得到列表,可通过list函数

a = range(5)
list(a)
创建列表的另外一种方法(列表推导式)

In [21]: testList = [x+2 for x in range(5)]
In [22]: testList
Out[22]: [2, 3, 4, 5, 6]
 
  

2.2map函数

map函数`会根据提供的`函数`对`指定序列`做`映射
map(function, sequence[, sequence, ...]) -> list
​
print(help(map))
Help on class map in module builtins:
​
class map(object)
 |  map(func, *iterables) --> map object
 |  
 |  Make an iterator that computes the function using arguments from
 |  each of the iterables.  Stops when the shortest iterable is exhausted.
 |  
 |  Methods defined here:
 |  
 |  __getattribute__(self, name, /)
 |      Return getattr(self, name).
 |  
 |  __iter__(self, /)
 |      Implement iter(self).
 |  
 |  __next__(self, /)
 |      Implement next(self).
 |  
 |  __reduce__(...)
 |      Return state information for pickling.
 |  
 |  ----------------------------------------------------------------------
 |  Static methods defined here:
 |  
 |  __new__(*args, **kwargs) from builtins.type
 |      Create and return a new object.  See help(type) for accurate signature.
​
None
​

说明:

  • function:是一个函数的引用,即函数名,而不是函数名()

  • sequence:是一个或多个序列,取决于function需要几个参数

  • 返回值:是一个列表

参数序列中的每一个元素分别调用function函数,将每次function函数的返回值,存到列表中当做最后的结果

案例1(基本使用)

# 函数需要一个参数
map(lambda x: x*x, [1, 2, 3])
# 结果为:[1, 4, 9]

每次从列表[1, 2, 3]中提取一个数值,然后当做实参传递到匿名函数中,将这个函数的返回值存储到列表中,

  1. 第一次取[1, 2, 3]中的第1个数据1当做实参传到匿名函数中,将1*1存储到列表中,此时列表为[1]

  2. 第一次取[1, 2, 3]中的第2个数据2当做实参传到匿名函数中,将2*2存储到列表中,此时列表为[1,4]

  3. 第三次取[1, 2, 3]中的第3个数据3当做实参传到匿名函数中,将3*3存储到列表中,此时列表为[1,4,9]

案例2(多个参数的情况)

# 函数需要两个参数
map(lambda x, y: x+y, [1, 2, 3], [4, 5, 6])
# 结果为:[5, 7, 9]
<

你可能感兴趣的:(python,学习,笔记,python,开发语言)