{ "cells": [ { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" }, "toc": "true" }, "source": [ "# Table of Contents\n", "
" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "# Python 面向对象进阶" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "## 类成员\n", "\n", "类的成员可以分为三大类:字段、方法和属性" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "![类成员信息](class_info.png)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "注:所有成员中,只有普通字段的内容保存对象中,即:根据此类创建了多少对象,在内存中就有多少个普通字段。而其他的成员,则都是保存在类中,即:无论对象的多少,在内存中只创建一份。" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## 字段\n", "\n", "字段包括:普通字段和静态字段,他们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同,\n", "\n", "- 普通字段属于对象\n", "- 静态字段属于类" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "湖南\n" ] }, { "data": { "text/plain": [ "'中国'" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "class Province:\n", "\n", " # 静态字段(属于类)\n", " country = '中国'\n", "\n", " def __init__(self, name):\n", "\n", " # 普通字段(属于对象)\n", " self.name = name\n", "\n", "\n", "# 直接访问普通字段\n", "obj = Province('湖南')\n", "print(obj.name) \n", "\n", "# 直接访问静态字段\n", "Province.country" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "由上述代码可以看出【普通字段需要通过对象来访问】【静态字段通过类访问】,在使用上可以看出普通字段和静态字段的归属是不同的。其在内容的存储方式类似如下图:" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "![类字段](class_field.png)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "由上图可是:\n", "\n", "- 静态字段在内存中只保存一份\n", "- 普通字段在每个对象中都要保存一份\n", "\n", "应用场景: 通过类创建对象时,如果每个对象都具有相同的字段,那么就使用静态字段" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "## 方法\n", "\n", "方法包括:普通方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同。\n", "\n", "- 普通方法:由对象调用;至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self;\n", "- 类方法:由类调用; 至少一个cls参数;执行类方法时,自动将调用该方法的类复制给cls;\n", "- 静态方法:由类调用;无默认参数;" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "class Foo:\n", "\n", " def normal_func(self):\n", " \"\"\" 定义普通方法,至少有一个self参数 \"\"\"\n", "\n", " # print self.name\n", " print('普通方法')\n", "\n", " @classmethod\n", " def class_func(cls):\n", " \"\"\" 定义类方法,至少有一个cls参数 \"\"\"\n", "\n", " print('类方法')\n", "\n", " @staticmethod\n", " def static_func():\n", " \"\"\" 定义静态方法 ,无默认参数\"\"\"\n", "\n", " print('静态方法')\n", "\n", "\n", "\n", "# 调用普通方法\n", "f = Foo()\n", "f.normal_func()\n", "\n", "# 调用类方法\n", "Foo.class_func()\n", "\n", "# 调用静态方法\n", "Foo.static_func()" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "**相同点:** 对于所有的方法而言,均属于类(非对象)中,所以,在内存中也只保存一份。\n", "\n", "**不同点:** 方法调用者不同、调用方法时自动传入的参数不同。" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## 属性\n", "\n", "如果你已经了解Python类中的方法,那么属性就非常简单了,因为Python中的属性其实是普通方法的变种,像使用字段那样简单的使用,但是又能像方法一样做一些运算。\n", "\n", "对于属性,有以下两个知识点:\n", "\n", "- 属性的基本使用\n", "- 属性的两种定义方式" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "**属性的基本使用:**" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "text/plain": [ "'normal'" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "'property'" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# ############### 定义 ###############\n", "class Foo:\n", "\n", " def func(self):\n", " return \"normal\"\n", "\n", " # 定义属性\n", " @property\n", " def prop(self):\n", " return \"property\"\n", " \n", " \n", "# ############### 调用 ###############\n", "foo_obj = Foo()\n", "\n", "foo_obj.func()\n", "foo_obj.prop #调用属性" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "由属性的定义和调用要注意一下几点:\n", "\n", "定义时,在普通方法的基础上添加 @property 装饰器;\n", "\n", "定义时,属性仅有一个self参数\n", "\n", "调用时,无需括号\n", "- 方法:foo_obj.func()\n", "- 属性:foo_obj.prop\n", "\n", "注意:属性存在意义是:访问属性时可以制造出和访问字段完全相同的假象,属性由方法变种而来,如果Python中没有属性,方法完全可以代替其功能。" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "**实例:**对于主机列表页面,每次请求不可能把数据库中的所有内容都显示到页面上,而是通过分页的功能局部显示,所以在向数据库中请求数据时就要显示的指定获取从第m条到第n条的所有数据(即:limit m,n),这个分页的功能包括:\n", "\n", "- 根据用户请求的当前页和总数据条数计算出 m 和 n\n", "- 根据m 和 n 去数据库中请求数据 " ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "text/plain": [ "0" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "10" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# ############### 定义 ###############\n", "class Pager:\n", "\n", " def __init__(self, current_page):\n", " # 用户当前请求的页码(第一页、第二页...)\n", " self.current_page = current_page\n", " # 每页默认显示10条数据\n", " self.per_items = 10\n", "\n", "\n", " @property\n", " def start(self):\n", " val = (self.current_page - 1) * self.per_items\n", " return val\n", "\n", " @property\n", " def end(self):\n", " val = self.current_page * self.per_items\n", " return val\n", "\n", "# ############### 调用 ###############\n", "\n", "p = Pager(1)\n", "p.start # 就是起始值,即:m\n", "p.end # 就是结束值,即:n" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "> 从上述可见,Python的属性的功能是:属性内部进行一系列的逻辑计算,最终将计算结果返回。(比字段灵活)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "**属性的两种定义方式**\n", "\n", "属性的定义有两种方式:\n", "\n", "- 装饰器 即:在方法上应用装饰器\n", "- 静态字段 即:在类中定义值为property对象的静态字段" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "**装饰器方式:**在类的普通方法上应用 **@property** 装饰器:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "11\n", "11\n", "13\n", "del all_pager\n" ] } ], "source": [ "class Pager:\n", "\n", " def __init__(self, all_count):\n", " self.all_count = all_count\n", "\n", " @property\n", " def all_pager(self):\n", " a1, a2 = divmod(self.all_count, 10)\n", " if a2 == 0:\n", " return a1\n", " else:\n", " return a1 + 1\n", "\n", " @all_pager.setter\n", " def all_pager(self, value):\n", " self.all_count = value\n", "\n", " \n", " @all_pager.deleter\n", " def all_pager(self):\n", " del self.all_count\n", " print('del all_pager')\n", "\n", "\n", "\n", "p = Pager(101)\n", "ret = p.all_pager # 仿字段一样操作,获取值\n", "print(ret)\n", "\n", "print(p.all_pager)\n", "p.all_pager = 121 # 仿字段一样操作,设置值\n", "print(p.all_pager)\n", "\n", "del p.all_pager # 仿字段一样操作,删除值" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "**静态字段方式**: 创建值为property对象的静态字段" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Kevin\n" ] } ], "source": [ "class Foo:\n", "\n", " def get_bar(self):\n", " return 'Kevin'\n", "\n", " BAR = property(get_bar)\n", "\n", "obj = Foo()\n", "reuslt = obj.BAR # 自动调用get_bar方法,并获取方法的返回值\n", "print(reuslt)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "property的构造方法中有个四个参数\n", "\n", "- 第一个参数是方法名,调用 对象.属性 时自动触发执行方法\n", "- 第二个参数是方法名,调用 对象.属性 = XXX 时自动触发执行方法\n", "- 第三个参数是方法名,调用 del 对象.属性 时自动触发执行方法\n", "- 第四个参数是字符串,调用 对象.属性.__doc__ ,此参数是该属性的描述信息" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on class property in module builtins:\n", "\n", "class property(object)\n", " | property(fget=None, fset=None, fdel=None, doc=None) -> property attribute\n", " | \n", " | fget is a function to be used for getting an attribute value, and likewise\n", " | fset is a function for setting, and fdel a function for del'ing, an\n", " | attribute. Typical use is to define a managed attribute x:\n", " | \n", " | class C(object):\n", " | def getx(self): return self._x\n", " | def setx(self, value): self._x = value\n", " | def delx(self): del self._x\n", " | x = property(getx, setx, delx, \"I'm the 'x' property.\")\n", " | \n", " | Decorators make defining new properties or modifying existing ones easy:\n", " | \n", " | class C(object):\n", " | @property\n", " | def x(self):\n", " | \"I am the 'x' property.\"\n", " | return self._x\n", " | @x.setter\n", " | def x(self, value):\n", " | self._x = value\n", " | @x.deleter\n", " | def x(self):\n", " | del self._x\n", " | \n", " | Methods defined here:\n", " | \n", " | __delete__(self, instance, /)\n", " | Delete an attribute of instance.\n", " | \n", " | __get__(self, instance, owner, /)\n", " | Return an attribute of instance, which is of type owner.\n", " | \n", " | __getattribute__(self, name, /)\n", " | Return getattr(self, name).\n", " | \n", " | __init__(self, /, *args, **kwargs)\n", " | Initialize self. See help(type(self)) for accurate signature.\n", " | \n", " | __new__(*args, **kwargs) from builtins.type\n", " | Create and return a new object. See help(type) for accurate signature.\n", " | \n", " | __set__(self, instance, value, /)\n", " | Set an attribute of instance to value.\n", " | \n", " | deleter(...)\n", " | Descriptor to change the deleter on a property.\n", " | \n", " | getter(...)\n", " | Descriptor to change the getter on a property.\n", " | \n", " | setter(...)\n", " | Descriptor to change the setter on a property.\n", " | \n", " | ----------------------------------------------------------------------\n", " | Data descriptors defined here:\n", " | \n", " | __isabstractmethod__\n", " | \n", " | fdel\n", " | \n", " | fget\n", " | \n", " | fset\n", "\n" ] } ], "source": [ "help(property)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "class Foo:\n", "\n", " def __init__(self):\n", " self.value = None\n", " \n", " def get_bar(self):\n", " return self.value\n", "\n", " def set_bar(self, value):\n", " self.value = value\n", "\n", " def del_bar(self):\n", " del self.value\n", " \n", " # property(fget=None, fset=None, fdel=None, doc=None)\n", " BAR = property(get_bar, set_bar, del_bar, 'description...')\n", "\n", "obj = Foo()\n", "\n", "obj.BAR # 自动调用第一个参数中定义的方法:get_bar\n", "obj.BAR = \"liang\" # 自动调用第二个参数中定义的方法:set_bar方法,并将“liang”当作参数传入\n", "del obj.BAR # 自动调用第三个参数中定义的方法:del_bar方法" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "由于静态字段方式创建属性具有三种访问方式,我们可以根据他们几个属性的访问特点,分别将三个方法定义为对同一个属性:**获取、修改、删除**" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "160.0" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "class Goods(object):\n", "\n", " def __init__(self):\n", " # 原价\n", " self.original_price = 100\n", " # 折扣\n", " self.discount = 0.8\n", "\n", " def get_price(self):\n", " # 实际价格 = 原价 * 折扣\n", " new_price = self.original_price * self.discount\n", " return new_price\n", "\n", " def set_price(self, value):\n", " self.original_price = value\n", "\n", " def del_price(self):\n", " del self.original_price\n", "\n", " PRICE = property(get_price, set_price, del_price, '价格属性描述...')\n", "\n", "obj = Goods()\n", "obj.PRICE = 200 # 修改商品原价\n", "obj.PRICE # 获取商品价格\n", "del obj.PRICE # 删除商品原价" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## 类特殊成员\n", "\n", "上文介绍了Python的类成员以及成员修饰符,从而了解到类中有字段、方法和属性三大类成员,并且成员名前如果有两个下划线,则表示该成员是私有成员,私有成员只能由类内部调用。无论人或事物往往都有不按套路出牌的情况,Python的类成员也是如此,存在着一些具有特殊含义的成员,详情如下:" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "1、 \\__doc__\n", "\n", "表示类的描述信息" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 描述类信息,这是用于看片的神器\n" ] } ], "source": [ "class Foo:\n", " \"\"\" 描述类信息,这是用于看片的神器\"\"\"\n", "\n", " def func(self):\n", " pass\n", "\n", "print(Foo.__doc__)\n", "#输出:类的描述信息" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "2、 \\__module\\__ 和 \\__class__ \n", "\n", "- \\__module__ 表示当前操作的对象在哪个模块\n", "- \\__class__ 表示当前操作的对象的类是什么" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "__main__\n", "