drf 06——认证、权限、频率、排序、过滤、RBAC

认证

登录认证《————某些接口必须要登录以后才能访问
登录接口————》登录成功返回随机字符串————》携带随机字符串【认证】通过再继续访问接口
APIView源码————》三大认证实在视图类的方法之前执行的
# 写一个登录接口
    用户表	用户token表
    前端传入用户名密码--视图类--登录方法--
     --校验用户名密码--正确生成随机字符串存入数据库--把随机字符串返回给前端
# 再随便写一个接口 --登录后才能访问
# 写认证类
    1.写一个类 继承BaseAuthentication
    2.再类中重写authenticate方法
    3.在方法中做验证 如果通过--返回两个值  
    	            不通过--抛AuthenticationFailed异常
# 使用认证类
	局部使用-->视图类中
    	class BookView(APIView):
            authentication_classes = [LoginAuth, ]

	全局使用-->配置文件中
    	REST_FRAMEWORK = {
            # 全局使用认证类
            'DEFAULT_AUTHENTICATION_CLASSES':['app01.auth.LoginAuth',]
        }
        
        局部禁用
    	class BookView(APIView):
            authentication_classes = []

登录接口

# 模型类
class User(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=32)
    user_type = models.IntegerField(choices=((1, '管理员'), (2, '普通用户'), (3, '游客')), default=3)

class UserToken(models.Model):
    token_code = models.CharField(max_length=32)
    user = models.OneToOneField(to='User', on_delete=models.CASCADE)

路由

from .views import UserView, BookView
from rest_framework.routers import SimpleRouter
router = SimpleRouter()
router.register('user', UserView, 'user')
router.register('book', BookView, 'book')
urlpatterns = [
]
urlpatterns+=router.urls

视图类

class UserView(ViewSet):
    authentication_classes = []	 # 局部禁用 总不能登录也要先登录才能登录吧
	# 127.0.0.1:8080/user/user/login--->post
    @action(methods=['post',], detail=False)
    def login(self, request):
        username = request.data.get('username')
        password = request.data.get('password')
        user = User.objects.filter(username=username, password=password).first()
        if user:
            # 登录成功-->生成随机字符串-->uuid能够生成一个随机不重复字符串           
            token = str(uuid.uuid4())
            # 存到UserToken表内	有就更新 没有新建
            # defaults=None,**kwargs 根据传入的关键字参数去查 查到就用defaults给的更新	此处为user
            UserToken.objects.update_or_create(user=user, defaults={'token_code': token})
            return Response({'code': 100, 'msg': '登录成功', 'token_code': token})
        else:
            return Response({'code': 101, 'msg': '用户名或密码错误'})

认证类

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from .models import UserToken

class LoginAuth(BaseAuthentication):
    def authenticate(self, request):
        # 做验证 验证用户是否登录	把生成的token放到请求头里
        if request.method in ['POST', 'PUT', 'DELETE']:
            # 增删改需要登录才能操作
            token = request.query_params.get('token')
            # 拿到请求头里的token 去数据库查
            user_token_obj = UserToken.objects.filter(token_code=token).first()
            if user_token_obj:
		# 验证通过返回两个值————当前登录用户,token
                return user_token_obj.user, token
            else:
                raise AuthenticationFailed('登录才可以操作哦')

权限

登录成功后 但有的接口区分权限 有权限的人能才能操作
User表的user_type字段来区分权限
# 权限类的写法
    1.写一个类 继承BasePermission
    2.重写has_permission方法
    3.在has_permission中进行权限的判断
    	有权限返回True,没权限返回False
        返回的中文提示信息使用message字段标识

# 权限类的使用	
    1.局部使用--->视图类中
      class BookView(APIView):
            permission_classes = [PermissionAuth, ]

    2.全局使用--->配置文件
      REST_FRAMEWORK = {
            # 全局使用认证类
            'DEFAULT_PERMISSION_CLASSES':['app01.auth.PermissionAuth',]
        }

    3.局部禁用
      class BookView(APIView):
            permission_classes = []

权限类

from rest_framework.permissions import BasePermission
class PermissionAuth(BasePermission):
    message = '你没有权限'
    
    def has_permission(self, request, view):
        if request.method == 'DELETE':
            if request.user.user_type == 3:
                print('有权限 删除成功')
                return True
            else:
                print('没权限 删除失败')
                self.message = '你是 %s 用户, 不能操作'%request.user.get_user_type_display()
                return False
        else:
            print('没进行删除操作')
            return True

频率

# 某个接口,限制访问频率---->可以根据IP,用户id
# 频率类的编写
    1.写一个类 继承SimpleRateThrottle
    2.重写get_cache_key方法
    3.返回什么 就以什么做限制
    4.写一个类属性
    	scope = 'xxx'	# 这个属性值用于配置
    5.配置文件中配置:
        REST_FRAMEWORK = {
        # 频率类中scope对应的值
        'xxx':'3/m'   # 数字/s m h  d
        }
    6.局部使用和全局使用
    	-局部:视图类中
        	class BookView(APIView):
    			throttle_classes = [IPThrottle, ]
        -全局:配置文件
            REST_FRAMEWORK = {
            	"DEFAULT_THROTTLE_RATES": {
                	# 频率类中scope对应的值
                	'xxx': '3/m'  # 数字/s m h  d
            	},
            	'DEFAULT_THROTTLE_CLASSES':['app01.throttling.IPThrottle', ]
        	}                

频率类

class IPThrottle(SimpleRateThrottle):
    # 写一个类属性,字符串
    scope = 'xxx'
    def get_cache_key(self, request, view):
        # return 什么就以什么做限制  返回ip/返回用户id
        return request.META.get('REMOTE_ADDR')
        # return request.user.id  # 返回用户id

视图类

class BookView(APIView):
    throttle_classes = [IPThrottle, ]

    def get(self, request):
        return Response('ok')

    def throttled(self, request, wait):
        from rest_framework.exceptions import Throttled
        class MyThrottled(Throttled):
            default_detail = '超限制了'
            extra_detail_singular = '还有 {wait} 秒.'
            extra_detail_plural = '超出了 {wait} 秒.'

        raise MyThrottled(wait)

配置文件

REST_FRAMEWORK = {
    "DEFAULT_THROTTLE_RATES": {
        # 频率类中scope对应的值
        '3_min': '3/m',  # 数字/s m h  d
        'anon':'4/h',
        'user':'5/m'
    },
    'DEFAULT_THROTTLE_CLASSES':['app01.throttling.IPThrottle',]
}

自定义频率类

# 逻辑: 
    1. 取出访问者ip	 {192.168.1.12:[访问时间3,访问时间2,访问时间1],192.168.1.12:[],192.168.1.14:[]}
    2. 判断ip在不在访问字典内,不在则添加进去 并加入第一次访问时间
    3. 不是第一次 则根据ip取出访问时间的列表	循环判断列表不为空,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
    4. 判断列表<3 说明访问次数不足三次 将当前时间插到列表第一个位置 返回True 表示通过
    5. >=3 说明访问超过三次 返回False验证失败
class MyThrottling():
    visit_record = {}

    def __init__(self):
        self.history = None

    def allow_request(self, request, view):
        ip = request.META.get('REMOTE_ADDR')        # 1.拿到ip
        import time
        ctime = time.time()

        # 2.如果ip不在访问字典里 表示第一次访问 将ip,和第一次访问时间添加进去
        if ip not in self.visit_record:
            self.visit_record[ip] = [ctime, ]
            #  {192.168.1.12:[访问时间3,访问时间2,访问时间1],192.168.1.12:[],192.168.1.14:[]}
            return True
        # 3.不是第一次 则 取出ip对应的时间列表
        self.history = self.visit_record.get(ip)        # 时间列表[时间3,时间2,时间1]
        while self.history and ctime - self.history[-1] > 60:  # 将现在时间和列表从后往前比 超过60s的去掉
            self.history.pop()

        # 4.列表<3  说明访问不足三次  便把当前时间插到列表第一个 返回True
        if len(self.history) < 3:
            self.history.insert(0, ctime)
            return True
        else:
            return False

    def wait(self):
        import time
        ctime = time.time()
        return 60 - (ctime - self.history[-1])

drf内置认证类、权限类、频率类

# 内置的认证---》跟咱们项目都不贴和,咱们不用,咱们自己根据自己的规则写
    -SessionAuthentication:之前老的session认证登录方式用,后期都不用了
    -BasicAuthentication :基本认证方式,咱们不用
    -TokenAuthentication :使用token认证方式,有用,但是咱们也是自己写的    
    
# 内置的权限类
    -IsAdminUser :校验是不是auth的超级管理员权限
    -IsAuthenticated:后面会用到,验证用户是否登录了,登录了才有权限,没登录就没有权限
    -IsAuthenticatedOrReadOnly:知道有这个东西即可
    
# 内置的频率类
    -UserRateThrottle :限制登录用户的频率,需要配置配置文件
    -AnonRateThrottle:登录用户不限制,未登录用户限制,需要配置配置文件

认证类、权限类、频率类源码分析

# 研究的第一个点:三大认证的执行顺序
    1.APIView--->dispatch--->三大认证
  	self.initial(request, *args, **kwargs)	# 执行三大认证
     	'self'为视图类的对象----如BookView的对象

    2.研究initial方法:执行了三个方法
    	self.perform_authentication(request)	# 认证
        self.check_permissions(request)		# 权限
        self.check_throttles(request)	# 频率        
# 相关源码
class APIView(View):
    def dispatch(self, request, *args, **kwargs):
    	......
        try:
            self.initial(request, *args, **kwargs)	# 先认证再执行请求
            if request.method.lower() in self.http_method_names:
                ...
    
    def initial(self, request, *args, **kwargs):
        ......
        self.perform_authentication(request)
        self.check_permissions(request)
        self.check_throttles(request)

认证类

# 为什么写了认证类,配置在视图类上,就会走认证?
# 入口:认证类怎么执行的---->self.perform_authentication(request)
    1.self.perform_authentication源码
    	def perform_authentication(self, request):
		    request.user
            
    2.user是新的request类中的 user(属性?方法?)
    	---->'Request'类内找到user 是一个方法 但加了@property
        @property
        def user(self):
            if not hasattr(self, '_user'):
                with wrap_attributeerrors():
                    self._authenticate()	# 一开始没有_user 于是执行这句
            return self._user        
        
    3.self._authenticate()  ---->self为Request的对象---->执行的是Request的_authenticate方法
    	def _authenticate(self):
            # authenticator配置的认证类的对象
            for authenticator in self.authenticators:	# 见第四点
                # 你配置在视图类上authentication_classes = [你写的认证类,]---->[你写的认证类1(),你写的认证类2()]	
                try:
                    # 调用认证类对象的authenticate方法 传了两个参数!一个认证类自己 一个self(Request类的对象)
                    user_auth_tuple = authenticator.authenticate(self)
                except exceptions.APIException:	 # 抛的是AuthenticationFailed,捕获的是APIException	
                    self._not_authenticated()
                    raise

                if user_auth_tuple is not None:
                    self._authenticator = authenticator
                    # 如果返回了两个值,第一个值给了request.user,第二个值给了request.auth
                    self.user, self.auth = user_auth_tuple
                    return
            self._not_authenticated()
        
    4.self.authenticators到底是啥?
    	# 你配置在视图类上authentication_classes = [你写的认证类,]---->[你写的认证类1(),你写的认证类2()]		
        Reqeust这个类实例化的时候,传入的,如果不传就是空元组
        找Request的实例化----> dispatch中包装了新的Request  
        request = self.initialize_request(request, *args, **kwargs)
        authenticators=self.get_authenticators()
        
        # APIView中的get_authenticators
        return [auth() for auth in self.authentication_classes]
    	self.authentication_classes为配在视图类上的认证类列表
点击查看相关源码
# 相关源码
class APIView(View):
    def perform_authentication(self, request):
	request.user

    def get_authenticators(self):
        return [auth() for auth in self.authentication_classes]

    def initialize_request(self, request, *args, **kwargs):
        ...
        return Request(
            request,
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),	这里 !!!!!!!!!!	
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )
    def dispatch(self, request, *args, **kwargs):
        request = self.initialize_request(request, *args, **kwargs)
        
    
    
class Request:
    def __init__(self,request...authenticators=None...):
        self._request = request
    	self.authenticators = authenticators or ()
        
    @property
    def user(self):
        if not hasattr(self, '_user'):
            with wrap_attributeerrors():
                self._authenticate()	# 一开始没有_user 于是执行这句
                return self._user  
            
    def _authenticate(self):
        for authenticator in self.authenticators:
            try:
                user_auth_tuple = authenticator.authenticate(self)
            except exceptions.APIException:
                self._not_authenticated()
                raise

            if user_auth_tuple is not None:
                self._authenticator = authenticator
                self.user, self.auth = user_auth_tuple
                return
        self._not_authenticated()

权限类

  1. 执行权限类 self.check_permissions(request)

    def check_permissions(self, request):
        for permission in self.get_permissions(): # 配在视图类上的权限类列表对象
            if not permission.has_permission(request, self):
                self.permission_denied(
                    request,
                    message=getattr(permission, 'message', None),
                    code=getattr(permission, 'code', None)
                )

  2. 研究self.get_permissions()
    def get_permissions(self):
        return [permission() for permission in self.permission_classes]

频率类

  1. self.check_throttles(request)
    def check_throttles(self, request):
        throttle_durations = []
        for throttle in self.get_throttles(): # 配在视图类上频率类列表 频率类的对象
            if not throttle.allow_request(request, self):
                throttle_durations.append(throttle.wait())
        if throttle_durations:
            durations = [
                duration for duration in throttle_durations
                if duration is not None
            ]

            duration = max(durations, default=None)
            self.throttled(request, duration)
            
            
# 自定义频率类一定要重写allow_request, 返回True就是没有频率显示,返回False就是被频率限制了

# 联想我们写过的继承SimpleRateThrottle的类
	SimpleRateThrottle重写了allow_request方法
    
class SimpleRateThrottle(BaseThrottle):
  	
    def __init__(self):
        if not getattr(self, 'rate', None):
            self.rate = self.get_rate()	# self.rate现在是 '3/m'
             # 3		 60
        self.num_requests, self.duration = self.parse_rate(self.rate)  # 从parse_rate方法获取
  
    def get_rate(self):    # 
        if not getattr(self, 'scope', None):
            msg = ("You must set either `.scope` or `.rate` for '%s' throttle" %
                   self.__class__.__name__)
            raise ImproperlyConfigured(msg)

        try:
            return self.THROTTLE_RATES[self.scope]
        except KeyError:
            msg = "No default throttle rate set for '%s' scope" % self.scope
            raise ImproperlyConfigured(msg)

    def parse_rate(self, rate):
        if rate is None:
            return (None, None)
        num, period = rate.split('/')
        num_requests = int(num)
        duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]
        return (num_requests, duration)
        
    def allow_request(self, request, view):        
        # 只要配置文件配了 就有值 在init中
        if self.rate is None:	
            return True
	# 现在的唯一字符串	ip地址
        self.key = self.get_cache_key(request, view)
        if self.key is None:
            return True
	# [时间2, 时间1]
        self.history = self.cache.get(self.key, [])
        self.now = self.timer()

        while self.history and self.history[-1] <= self.now - self.duration:
            self.history.pop()	# 把和现在差60s的数据都剔除 self.history只剩60s内的访问时间
        if len(self.history) >= self.num_requests:	# >=配置的数字 3
            return self.throttle_failure()	# return False
        return self.throttle_success()	# 把当前的时间插入 return True

排序

# 排序功能接口只针对于:获取所有接口

# 继承了GenericAPIView的视图类,只要加入,两个类属性
class BookView(GenericViewSet, ListModelMixin):
    serializer_class = BookSerializer
    queryset = Book.objects.all()

    filter_backends = [OrderingFilter, ]
    ordering_fields = ['price','id']

# 访问的时候
http://127.0.0.1:8000/api/v1/books?ordering=price  	# 按price升序
http://127.0.0.1:8000/api/v1/books?ordering=-price 	# 按price降序
http://127.0.0.1:8000/api/v1/books?ordering=price,id    # 先按价格升序排,价格一样,再按id升序排    

过滤

1. 内置的过滤使用:不能指定查询哪个字段 '模糊查询'
    继承了GenericAPIView的视图类,只要加入,两个类属性
    
class BookView(GenericViewSet, ListModelMixin):
    serializer_class = BookSerializer
    queryset = Book.objects.all()

    filter_backends = [SearchFilter,]
    search_fields=['name','price']  # 按name或price过滤    

# 使用
	http://127.0.0.1:8000/api/v1/books?search=关键字	# name like xx or price like xx
                                                
2. 第三方django-filter: 指定字段精确查询
    -安装:pip3 install django-filter
    继承了GenericAPIView的视图类,只要加入,两个类属性

from django_filters.rest_framework import DjangoFilterBackend
class BookView(GenericViewSet, ListModelMixin):
    serializer_class = BookSerializer
    queryset = Book.objects.all()

    filter_backends = [DjangoFilterBackend, ]
    filter_fields = ['name', 'price']    
    
# 使用    
        http://127.0.0.1:8000/api/v1/books?name=三国&price=11
3.自定义过滤器---->完成更多查询操作
    -写一个类,继承BaseFilterBackend
    -重写filter_queryset方法
    -配置在视图类中

from .throttling import FilterName
class BookView(GenericViewSet, ListModelMixin):
    serializer_class = BookSerializer
    queryset = Book.objects.all()

    filter_backends = [FilterName, ]

# 既有过滤又有排序
class BookView(GenericViewSet, ListModelMixin):
    serializer_class = BookSerializer
    queryset = Book.objects.all()
    filter_backends = [FilterName, OrderingFilter]
    ordering_fields = ['price', ]
    
# 源码分析,为什么这么配置就生效
    -GenericAPIView的方法

def filter_queryset(self, queryset):
    for backend in list(self.filter_backends):
        queryset = backend().filter_queryset(self.request, queryset, self)
    return queryset

全局异常处理

drf配置文件中 已经配置了 但不符合我们想要的要求
# drf的配置文件:
    'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler'
     如果抛了异常 就会执行exception_handler函数
    
现在我们重写一个函数 抛异常时 执行我们写的函数

# drf默认的异常处理 只处理了drf自己的异常:
    所有drf中抛的异常	都有detail,django的异常	抛出很长的xml数据
    
{
    "detail": "it's wrong"
}    

# 我们想要的
{
    "code": 999,
    "msg": "it's wrong"
}

使用步骤

1.写一个函数
from rest_framework.views import exception_handler
from rest_framework.response import Response
def common_exception_handler(exc, context):
    # 正常来讲,在这里需要记录日志---》如何在django中记录日志后面讲
    # 日志记录,越详细越好:哪个用户(id,ip),在什么时间,执行哪个视图函数时报了错,请求地址是什么
    print(context['view'])  # 视图类对象
    print(context['request'])   # 当前请求对象----->可以取出ip,用户id,当前时间,请求地址来
    view = context['view']
    request = context['request']
    print('ip地址为: %s 的用户 访问了:%s 视图类, 报错 请求地址为:%s' % (request.META.get('REMOTE_ADDR'), str(view), request.path))

    response = exception_handler(exc, context)
    if response:    # 这是drf的异常 drf已处理 但不是我们想要的格式{'code': 100, 'msg': ''}
        res = Response({'code': 999, 'msg': request.data.get('detail')})
    else:
        # res=Response({'code':998,'msg':'服务器错误,请联系系统管理员'})
        res = Response({'code': 998, 'msg': str(exc)})
    return res

2.把函数配置在配置文件中
REST_FRAMEWORK = {
    # 自己写的全局异常捕获
        'EXCEPTION_HANDLER': 'app01.exception.common_exception_handler',
    }

自动生成接口文档

# 后端人员,写好接口,提供接口文档给前端用

# 如何编写接口文档
    1. 使用word写,md写---->提交到git上
    2.公司有接口文档平台---->后端人员在文档平台录入数据---->公司自己开发,yapi(百度开源),第三方
    3.自动生成接口文档---->项目写好了,一键生成接口文档---->一键生成---->导出---->导入到yapi
    
# drf中自动生成接口文档
    -coreapi,swagger(更通用一些,go,java)
    -安装 pip3 install coreapi
    -在项目中配置	
    	-加入路由:
        from rest_framework.documentation import include_docs_urls
        urlpatterns = [
            path('docs/', include_docs_urls(title='站点页面标题'))
        ]
        -配置文件中配置
        REST_FRAMEWORK = {
         'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',

        }
        
        -尽管写接口,写注释,会自动生成
	

RBAC介绍

# RBAC
	基于角色的访问控制(Role-Based Access Control)
    在RBAC中:
    	权限与角色相关联 用户通过成为适当角色的成员而获得角色的权限
        这就极大地简化了权限的管理。
        这样管理都是层级相互依赖的,权限赋予给角色,而把角色又赋予用户,这样的权限设计很清楚,管理起来很方便
        
# 这种设计,在公司内部系统用的多,对外的系统基本不用

# 权限:真正的权限,比如发工资,招人,申请测试机
# 角色:(组,部门) 角色下有很多员工
# 用户:一个个的人

# 用户和角色关系是多对多,中间表
# 权限和角色关系是多对多,中间表
# 权限和用户的中间表

#前后端分离项目控制权限---》权限类中
#前后端混合
    -django框架在公司里用的很多,写内部项目,肯定要用权限控制,用的就是rbac,自己实现一套,django内置了后台管理,自带rbac(admin+auth)
    -基于django的admin二次开发
    	-美化:xadmin(基本不用了,2.x以后django不兼容多,作者弃坑了)
        -simpleui:
    -django+drf+vue=djagnovueadmin  一款前后端分离的自带rbac的后台管理框架
    
# django的admin基于rbac
    -auth_user  	 # 用户表
    -auth_permission  # 权限表
    -auth_group  	 # 组,角色表
    
    -auth_user_groups   # 用户和组中间表
    -auth_group_permissions  # 组和权限中间表
    -auth_user_user_permissions  # 用户和权限中间表    

补充1

# external libraries
    -Python 解释器--内置模块和包:os,sys,json 。。
    -import os
    -装的第三模块:site-packages中
    	-真正的位置:python解释器安装路径下的site-package中
        -有些第三方模块一安装---》释放可执行文件---》一定要注意,释放的位置释放在环境变量里
        
# 安装第三方模块
    -pychram中图形化界面装-----》必须有pycharm---》换源
    -pip install django==2.2.2 -i 国内源
     -敲的pip 是哪个解释器的pip 确认好
        
# 换源
    -装第三方模块---》本质就是从远端某个位置拉了一个包 xx.whl--->释放到了python解释器的site-package文件夹下,把可执行文件释放到scripts文件夹下
    -用官方源:在国外---》下载慢
    -使用国内镜像:豆瓣,阿里,清华

回顾

# http回顾
    -http 请求:特点
    -1 http基于tcp协议之上的协议---->tcp处于osi七层的传输层  http处于应用层
    -2 基于请求响应----必须是客户端发起请求,服务端响应	不能服务端主动推送信息
    -3 无状态,无连接	cookie和session
    -http协议有版本
    	-0.9  1.1  2.x:多路复用   3.x 
            
	-http分请求协议和响应协议
    	-请求协议
            -请求首行:  请求方式,请求地址(get地址中数据),协议版本
            -请求头:	key:value	客户端类型,客户端ip地址,请求编码,cookie...
	    -请求体: 所谓的body体,posy,put请求真正携带的数据
                    -urlencoded:  name=lqz&age=19&gender=male
                    -json :   {"name":"lqz","age":19,"gender":"male"}
                    -form-data: 两部分:数据部分,文件部分中间用  -----------分割

	-响应协议:
            -响应首行:   协议版本,响应状态码,响应状态短语
            -响应头:     响应编码格式,cookie...
            -响应体:     html,json,xml.....

# websocket协议	应用层协议
	-服务端主动向客户端推送的情况,使用websocket

# http请求 轮询
# http请求 长轮询:发过去,等一会,再回来
# websocket 协议主动推送    

补充2

python	动态强类型语言
go	静态强类型语言
java	静态强类型语言	---隐式类型转换
js	动态弱类型语言

# 动态:解释性语言
# 强弱:
     数据类型强:不同类型之间不允许直接运算
     数据类型弱:不同类型之间不需要转换可以直接运算

补充3

# 鸭子类型(面试重点)
    -接口概念:规范子类的行为   你当成:父类,有一些方法  ---》Duck类,speak,run方法
    -只要子类,继承了Duck,我们就认为,这些子类,他们是同一类,都是鸭子Duck这个类
    -python不推崇这个,推崇鸭子类型
    -现在只要有一个类,中有speak和run 方法 ,这个类就是鸭子类
    
    -abc装饰器,装饰父类中的方法,子类必须实现该方法,如果不实现,就抛异常---》正好违背了鸭子类型
    -djagno中常用的方式,父类抛异常的方式,子类必须重写该方法:
       def authenticate(self, request):
          raise NotImplementedError(".authenticate() must be overridden.")
# 函数
	def add(a:int,b:int):->int
        return a+b
posted @   扶我上码  阅读(60)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
点击右上角即可分享
微信分享提示