python 全栈开发,Day113(方法和函数的区别,yield,反射)
一、方法和函数的区别
面向对象
初级
class StarkConfig(object): def __init__(self,model_class): self.model_class = model_class def changelist_view(self,request): return 123 class RoleConfig(StarkConfig): def changelist_view(self,request): return 666 obj1 = StarkConfig('xiao') obj2 = RoleConfig('zhang') ret1 = obj1.changelist_view(1) ret2 = obj2.changelist_view(2) print(ret1) print(ret2)
执行输出:
123 666
中级
class StarkConfig(object): list_display = [] def __init__(self,model_class): self.model_class = model_class def changelist_view(self,request): return self.list_display class RoleConfig(StarkConfig): list_display = ['id','name'] obj1 = StarkConfig('xiao') obj2 = RoleConfig('zhang') ret1 = obj1.changelist_view(1) ret2 = obj2.changelist_view(2) print(ret1) print(ret2)
执行输出:
[] ['id', 'name']
注意:obj2的self指的是RoleConfig。因为实例化哪个对象,那么self就是那个对象!
所以ret2输出['id', 'name']
在昨天用到的include,它的本质就是返回一个元组,元组包含了3个元素
autodiscover_modules它干了啥?当程序启动时,去每个app目录下找stark.py,并加载!
函数和方法
函数
def func(): pass class Foo(object): def display(self): pass print(func) print(Foo.display)
执行输出:
<function func at 0x0000025E24C98F28> <function Foo.display at 0x0000025E24FF52F0>
输出function ,这2个都是函数
方法
class Foo(object): def display(self): pass obj = Foo() print(obj.display)
执行输出:
<bound method Foo.display of <__main__.Foo object at 0x000001F2C6193A90>>
输出bound method ,它是一个绑定方法。
注意:在类中定义的def,它到底是方法还是函数呢?取决于它的调用方式。
如果通过类名调用它,它就是函数。如果通过实例化对象,再调用,它就是方法。
代码判断
人为的判断,难免会出错。python提供了2个方法,专门用来做判断。
具体请看下面的代码
from types import MethodType,FunctionType def check(arg): """ 判断arg时函数则打印1,arg是方法打印2 :param arg: :return: """ if isinstance(arg,MethodType): print(2) elif isinstance(arg,FunctionType): print(1) else: print('错误!不支持该类型') def func(): pass class Foo(object): def display(self): pass check(func) check(Foo.display) obj = Foo() check(obj.display)
执行输出:
1 1 2
练习题
例1
class RoleConfig(object): def f1(self,arg): print('f1',arg) def f2(self,arg): print('f2',arg) list_display = [f1,f2] for item in RoleConfig.list_display: # print(item) item(1,2)
执行输出:
f1 2
f2 2
注意:这里的item是函数,它必须要传递2个参数。self也是一个参数!
例2
class RoleConfig(object): def f1(self,arg): print('f1',arg) def f2(self,arg): print('f2',arg) list_display = [f1,f2] obj = RoleConfig() for item in RoleConfig.list_display: # print(item) item(obj,2)
执行输出,效果同上!
注意:item(obj,2),这里面的obj是一个对象,指的是self。那么它再传一个参数,就可以了!
例3
class RoleConfig(object): def f1(self,arg): print('f1',arg) def f2(self,arg): print('f2',arg) def f3(self,arg): print('f3',arg) list_display = [f1,f2] def get_list_display(self): # insert() 函数用于将指定对象插入列表的指定位置 # 0表示第一个元素位置 self.list_display.insert(0,self.f3) return self.list_display obj = RoleConfig() for item in obj.get_list_display(): print(item) item(obj,2)
执行输出:
<bound method RoleConfig.f3 of <__main__.RoleConfig object at 0x000001864A87C780>> Traceback (most recent call last): File "E:/python_script/django框架/day18/untitled/a.py", line 67, in <module> item(obj,2) TypeError: f3() takes 2 positional arguments but 3 were given
明明只传了2个,为什么是3个呢?
注意:f3是bound method
定义了一个类RoleConfig,实例化RoleConfig得到了obj这个对象,然后调用这个对象方法obj.method(self,arg)
这个过程中Python会自动转为RoleConfig.mehod(obj,self,arg),实际它传递了3个参数!
为什么呢?f3是一个绑定方法,必须要将obj实例作为第一个参数!否则报错!
既然它是一个绑定方法,那么给它绑定一下。插入到列表时,使用RoleConfig.f3
class RoleConfig(object): def f1(self,arg): print('f1',arg) def f2(self,arg): print('f2',arg) def f3(self,arg): print('f3',arg) list_display = [f1,f2] def get_list_display(self): # insert() 函数用于将指定对象插入列表的指定位置 # 0表示第一个元素位置 self.list_display.insert(0,RoleConfig.f3) return self.list_display obj = RoleConfig() for item in obj.get_list_display(): print(item) item(obj,2)
执行输出:
<function RoleConfig.f3 at 0x0000021D6DB15400> f3 2 <function RoleConfig.f1 at 0x0000021D6D8B68C8> f1 2 <function RoleConfig.f2 at 0x0000021D6DB152F0> f2 2
例4
class RoleConfig(object): def f1(self,arg): print('f1',arg) def f2(self,arg): print('f2',arg) def f3(self,arg): print('f3',arg) list_display = [f1,f2] def get_list_display(self): # insert() 函数用于将指定对象插入列表的指定位置 # 0表示第一个元素位置 self.list_display.insert(0,RoleConfig.f3) return self.list_display obj1 = RoleConfig() for item in obj1.get_list_display(): # print(item) item(obj1,2) obj2 = RoleConfig() for item in obj2.get_list_display(): # print(item) item(obj1,6)
执行输出:
f3 2 f1 2 f2 2 f3 6 f3 6 f1 6 f2 6
为毛会输出7个呢?这不科学!
统计一下列表的数量
下面这段代码,详细解释了代码执行过程,以及list_display元素的变化
class RoleConfig(object): def f1(self,arg): print('f1',arg) def f2(self,arg): print('f2',arg) def f3(self,arg): print('f3',arg) list_display = [f1,f2] def get_list_display(self): # insert() 函数用于将指定对象插入列表的指定位置 # 0表示第一个元素位置 self.list_display.insert(0,RoleConfig.f3) return self.list_display obj1 = RoleConfig() # 此时list_display有2个元素 for item in obj1.get_list_display(): # 此时list_display有3个元素,因为执行了insert # 列表的值为f3,f1,f2 item(obj1,2) # 输出f3,f1,f2 print('第一次统计数量:',len(RoleConfig.list_display)) # 3个 obj2 = RoleConfig() # list_display还是有3个,列表的值为f3,f1,f2 for item in obj2.get_list_display(): # 此时list_display有4个元素,因为再次执行了insert # 列表的值为f3,f3,f1,f2 item(obj1,6) #输出f3,f3,f1,f1,f2 print('第二次统计数量:',len(RoleConfig.list_display))
执行输出:
f3 2 f1 2 f2 2 第一次统计数量: 3 f3 6 f3 6 f1 6 f2 6 第二次统计数量: 4
注意:最终list_display的元素数量为4个,并不是7个。只不过执行了2次for循环,所以才输出了7次。
例5
为了避免列表重复插入,定义一个空列表
class RoleConfig(object): def f1(self, arg): print('f1', arg) def f2(self, arg): print('f2', arg) def f3(self, arg): print('f3', arg) list_display = [f1, f2] def get_list_display(self): v = [] v.extend(self.list_display) v.insert(0,RoleConfig.f3) return v obj1 = RoleConfig() for item in obj1.get_list_display(): item(obj1,2) obj2 = RoleConfig() for item in obj2.get_list_display(): item(obj2,6)
执行输出:
f3 2 f1 2 f2 2 f3 6 f1 6 f2 6
那么它是如何避免列表重复插入的呢?
看下面的注释
num = 0 # 全局变量,统计列表数量 class RoleConfig(object): def f1(self,arg): print('f1',arg) def f2(self,arg): print('f2',arg) def f3(self,arg): print('f3',arg) list_display = [f1,f2] def get_list_display(self): global num # 要使用全局变量,必须声明global num += 1 # 自增1 v = [] # 定义空列表,每次执行时,列表重置为空列表 v.extend(self.list_display) # 列表扩展,此时v=[f1,f2] v.insert(0,RoleConfig.f3) # 第一个位置插入f3,此时v=[f3,f1,f2] print('第%s次统计数量:'%num, len(v)) return v obj1 = RoleConfig() # 此时v有0个元素 for item in obj1.get_list_display(): # 此时v有3个元素 item(obj1,2) obj2 = RoleConfig() # 此时v有0个元素 for item in obj2.get_list_display(): # 此时v有3个元素 item(obj1,6)
执行输出:
第1次统计数量: 3 f3 2 f1 2 f2 2 第2次统计数量: 3 f3 6 f1 6 f2 6
注意:虽然执行了2次for循环。但是每次执行get_list_display()方法时,v列表会被重置为空列表。
所以每次for循环,都是3个值!
二、yield
python中有一个非常有用的语法叫做生成器,所利用到的关键字就是yield。有效利用生成器这个工具可以有效地节约系统资源,避免不必要的内存占用
我们对一个生成器使用for循环时,它其实调用了__next__函数。也就是获取下一个元素!
注意:对生成器使用__next__函数时,如果没有下一个值,会报错!但是使用for循环,它内部使用了异常处理,所以我们不需要担心,到底下一个,有没有值!
Python的三大神器:装饰器.迭代器与生成器!可谓无人不知,无人不晓啊。以下省略一万字...
应用场景
例1
假设有1000条数据,用户名和密码拼接成一个字符串,页面打印出来。
要求前端页面不能拼接,只能后台拼接生成!
以前的你
def func(request): result = [] data_list = models.Users.objects.all() for row in data_list: temp = "%s%s" %(row.name,row.pwd) result.append(temp) return render(result,'xxx.html',{'result':result})
xxx.html
{% for row in result %} {{row}} {% endfor %}
它有一个问题,内存里面有2000条数据。占用内存太大了!
现在的你
def get_result(data_list): for row in data_list: temp = "%s%s" % (row.name, row.pwd) yield temp def func(request): data_list = models.Users.objects.all() result = get_result(data_list) return render(result, 'xxx.html', {'result': result})
xxx.html的代码,保持不变。还是用for循环!
页面加载时,for循环一次只加载一条数据
yeild取完之后,就消失了
在内存中,只有一条数据!这样,就可以节省内存!
三、反射
getattr(object, name[,default])
获取对象object的属性或者方法,如果存在打印出来,如果不存在,打印出默认值,默认值可选。
需要注意的是,如果是返回的对象的方法,返回的是方法的内存地址,如果需要运行这个方法,
可以在后面添加一对括号。
举例
新建一个项目untitled1,注意:django版本为1.11
修改models.py,增加表
from django.db import models # Create your models here. class UserInfo(models.Model): username = models.CharField(verbose_name="用户名",max_length=32)
使用2个命令,生成表
python manage.py makemigrations
python manage.py migrate
使用navicat添加3条数据
修改urls.py,增加路径
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^test/', views.test), ]
修改views.py,增加视图函数
from django.shortcuts import render,HttpResponse from app01 import models # Create your views here. def test(request): user_queryset = models.UserInfo.objects.all() for item in user_queryset: print(item.id,item.username) return HttpResponse('ok')
启动django项目,访问页面,效果如下:
查看Pycharm控制台输出:
1 韩雪 2 唐嫣 3 赵丽颖
反射对象
修改views.py,使用getattr
from django.shortcuts import render,HttpResponse from app01 import models # Create your views here. def test(request): user_queryset = models.UserInfo.objects.all() for item in user_queryset: print(item.id,item.username,getattr(item,'username')) return HttpResponse('ok')
刷新网页,查看Pycharm控制台输出:
1 韩雪 韩雪 2 唐嫣 唐嫣 3 赵丽颖 赵丽颖
显示指定的列
修改views.py,增加属性list_display
def test(request): list_display = ['id','username'] user_queryset = models.UserInfo.objects.all() for item in user_queryset: row = [] for field in list_display: row.append(getattr(item,field)) print(row) return HttpResponse('ok')
刷新网页,查看Pycharm控制台输出:
[1, '韩雪'] [2, '唐嫣'] [3, '赵丽颖']
只取id,修改list_display
from django.shortcuts import render,HttpResponse from app01 import models # Create your views here. def test(request): list_display = ['id'] user_queryset = models.UserInfo.objects.all() for item in user_queryset: row = [] for field in list_display: row.append(getattr(item,field)) print(row) return HttpResponse('ok')
刷新网页,查看Pycharm控制台输出:
[1] [2] [3]
获取verbose_name
修改views.py
from django.shortcuts import render,HttpResponse from app01 import models # Create your views here. def test(request): list_display = ['id','username'] models_class = models.UserInfo verbose_name = models.UserInfo._meta.get_field('username').verbose_name print(verbose_name) return HttpResponse('ok')
刷新网页,查看Pycharm控制台输出:
用户名
使用for循环,获取指定列的verbose_name
修改views.py
from django.shortcuts import render,HttpResponse from app01 import models # Create your views here. def test(request): list_display = ['id','username'] models_class = models.UserInfo for name in list_display: verbose_name = models.UserInfo._meta.get_field(name).verbose_name print(verbose_name) return HttpResponse('ok')
刷新网页,查看Pycharm控制台输出:
ID
用户名
注意:在models.py中的UserInfo虽然没有定义id字段。但是它会默认创建id字段。它的verbose_name是大写的ID
同时打印指定字段的verbose_name以及表记录
修改views.py
from django.shortcuts import render,HttpResponse from app01 import models # Create your views here. def test(request): list_display = ['id','username'] header_list = [] # models_class = models.UserInfo for name in list_display: verbose_name = models.UserInfo._meta.get_field(name).verbose_name header_list.append(verbose_name) print(header_list) user_queryset = models.UserInfo.objects.all() for item in user_queryset: row = [] for field in list_display: row.append(getattr(item, field)) print(row) return HttpResponse('ok')
刷新网页,查看Pycharm控制台输出:
['ID', '用户名'] [1, '韩雪'] [2, '唐嫣'] [3, '赵丽颖']
去掉id字段
修改views.py
from django.shortcuts import render,HttpResponse from app01 import models # Create your views here. def test(request): list_display = ['username'] header_list = [] # models_class = models.UserInfo for name in list_display: verbose_name = models.UserInfo._meta.get_field(name).verbose_name header_list.append(verbose_name) print(header_list) user_queryset = models.UserInfo.objects.all() for item in user_queryset: row = [] for field in list_display: row.append(getattr(item, field)) print(row) return HttpResponse('ok')
刷新网页,查看Pycharm控制台输出:
['用户名'] ['韩雪'] ['唐嫣'] ['赵丽颖']
通过这样,就可以自定义页面显示的列。为后续的stark组件做准备工作!