【问题标题】:functools stops working when switch to Python 3functools 在切换到 Python 3 时停止工作
【发布时间】:2019-03-17 05:56:14
【问题描述】:

我们已经将我们的 Django 项目代码库从 Python 2.7 迁移到 3.6,但突然之间,以前的工作停止了。具体来说:

map(functools.partial(self._assocUser, user=user), persistedGroupIds)

需要替换为:

 for group_id in persistedGroupIds:
      self._assocUser(group_id, user)

还有这个:

    persistedGroupIds = map(functools.partial(self._persistGroup, grp_mappings=attrAll.entitlements), saml_authorization_attributes)  

需要前往:

     persistedGroupIds = []
     for idp_group_name in saml_authorization_attributes:
         persistedGroupIds.append(self._persistGroup(idp_group_name, attrAll.entitlements))

在旧功能重新出现之前。 Python 3 的 functools 似乎不起作用。

以下是在 Python 2 下运行良好的代码的完整列表:

    from django.contrib.auth.models import User
from django.contrib.auth.models import Group
import functools
from mappings import SAMLAttributesConfig
from django.conf import settings
import logging

log = logging.getLogger(__name__)

class SAMLServiceProviderBackend(object):

    empty_entitlements_message="IdP supplied incorrect authorization entitlements.  Please contact their support."

    def _assocUser(self, group_id, user):

        group = Group.objects.get(id=group_id)
        group.user_set.add(user)

        return None


    def _persistGroup(self,idp_group_name, grp_mappings):

        group_name = grp_mappings[idp_group_name]

        try:
            group = Group.objects.get(name=group_name)
        except Group.DoesNotExist:
            group = Group(name=group_name)
            group.save()

        return group.id

    def _extract_grp_entitlements(self,saml_authentication_attributes,groups):
        result = []
        input_length = len(saml_authentication_attributes[groups])
        if input_length == 0:
            log.error(self.empty_entitlements_message)
            raise RuntimeError(self.empty_entitlements_message)
        if input_length == 1:
            result = [t.strip() for t in saml_authentication_attributes[groups][0].split(',')] 
        elif input_length:
            result = saml_authentication_attributes[groups]
        return result
#         return [t.strip() for t in saml_authentication_attributes[groups][0].split(',')] \
#             if len(saml_authentication_attributes[groups]) == 1\
#             else saml_authentication_attributes[groups]


    def authenticate(self, saml_authentication=None):
        if not saml_authentication:  # Using another authentication method
            return None

        attrAll = SAMLAttributesConfig(mappings_file_name=settings.AUTH_MAPPINGS_FILE).get_config()
        groups = attrAll.entitlements.containerName

        if saml_authentication.is_authenticated():

            saml_authentication_attributes = saml_authentication.get_attributes()
            saml_authorization_attributes = self._extract_grp_entitlements(saml_authentication_attributes,groups)          
            persistedGroupIds = map(functools.partial(self._persistGroup, grp_mappings=attrAll.entitlements), saml_authorization_attributes)  

            try:
                user = User.objects.get(username=saml_authentication.get_nameid())
            except User.DoesNotExist:

                user = User(username=saml_authentication.get_nameid())
                user.set_unusable_password()
                try:
                    user.first_name = saml_authentication_attributes['samlNameId'][0]
                except KeyError:
                    pass
                try:
                    setattr(user, "first_name", saml_authentication_attributes[attrAll.subject.first_name][0])

                except KeyError:
                    pass 

                #user.last_name = attributes['Last name'][0]
                user.save()
                map(functools.partial(self._assocUser, user=user), persistedGroupIds)
                user.save()
            return user
        return None

    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None

上面的代码在 Python 3 环境下不再工作,只能在类似这样的情况下开始工作,在 for 循环中拼写出 functools.partial() 调用:

from django.contrib.auth.models import User
from django.contrib.auth.models import Group
import functools
from .mappings import SAMLAttributesConfig
from django.conf import settings
import logging

log = logging.getLogger(__name__)

class SAMLServiceProviderBackend(object):

    empty_entitlements_message="IdP supplied incorrect authorization entitlements.  Please contact their support."

    def _assocUser(self, group_id, user):

        group = Group.objects.get(id=group_id)
        group.user_set.add(user)

        return None


    def _persistGroup(self,idp_group_name, grp_mappings):

        group_name = grp_mappings[idp_group_name]

        try:
            group = Group.objects.get(name=group_name)
        except Group.DoesNotExist:
            group = Group(name=group_name)
            group.save()

        return group.id

    def _extract_grp_entitlements(self,saml_authentication_attributes,groups):
        result = []
        input_length = len(saml_authentication_attributes[groups])
        if input_length == 0:
            log.error(self.empty_entitlements_message)
            raise RuntimeError(self.empty_entitlements_message)
        if input_length == 1:
            result = [t.strip() for t in saml_authentication_attributes[groups][0].split(',')] 
        elif input_length:
            result = saml_authentication_attributes[groups]
        return result
#         return [t.strip() for t in saml_authentication_attributes[groups][0].split(',')] \
#             if len(saml_authentication_attributes[groups]) == 1\
#             else saml_authentication_attributes[groups]


    def authenticate(self, saml_authentication=None):
        if not saml_authentication:  # Using another authentication method
            return None

        attrAll = SAMLAttributesConfig(mappings_file_name=settings.AUTH_MAPPINGS_FILE).get_config()
        groups = attrAll.entitlements.containerName

        if saml_authentication.is_authenticated():

            saml_authentication_attributes = saml_authentication.get_attributes()
            saml_authorization_attributes = self._extract_grp_entitlements(saml_authentication_attributes,groups)          
            persistedGroupIds = map(functools.partial(self._persistGroup, grp_mappings=attrAll.entitlements), saml_authorization_attributes)  

            try:
                user = User.objects.get(username=saml_authentication.get_nameid())
            except User.DoesNotExist:

                user = User(username=saml_authentication.get_nameid())
                user.set_unusable_password()
                try:
                    user.first_name = saml_authentication_attributes['samlNameId'][0]
                except KeyError:
                    pass
                try:
                    setattr(user, "first_name", saml_authentication_attributes[attrAll.subject.first_name][0])

                except KeyError:
                    pass 

                #user.last_name = attributes['Last name'][0]
                user.save()
                for group_id in persistedGroupIds:
                    self._assocUser(user = user, group_id = group_id)
                # map(functools.partial(self._assocUser, user=user), persistedGroupIds)
                user.save()
            return user
        return None

    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None

可能有什么问题?

我在 Eclipse 中使用 PyDev 插件。以下是我的 Python 解释器的配置方式:



这是 Eclipse 的 .pydevproject 文件:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?eclipse-pydev version="1.0"?><pydev_project>



    <pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">venv3.6</pydev_property>



    <pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python interpreter</pydev_property>



    <pydev_variables_property name="org.python.pydev.PROJECT_VARIABLE_SUBSTITUTION">

        <key>DJANGO_SETTINGS_MODULE</key>

        <value>reporting.settings</value>

        <key>DJANGO_MANAGE_LOCATION</key>

        <value>./manage.py</value>

        <key>SAML_PLUGIN</key>

        <value>/Users/sl/abc/venv3.6/lib/python3.6/site-packages/onelogin/saml2</value>

        <key>PY</key>

        <value>36</value>

    </pydev_variables_property>



    <pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">



        <path>/${PROJECT_DIR_NAME}</path>



    </pydev_pathproperty>



    <pydev_pathproperty name="org.python.pydev.PROJECT_EXTERNAL_SOURCE_PATH">

        <path>${SAML_PLUGIN}</path>

    </pydev_pathproperty>


</pydev_project>

【问题讨论】:

  • 出现错误或为什么说它不起作用?
  • 不,没有错误,但预期的功能不存在,测试开始中断等。换句话说,只有更改的后果是明显的,没有 Python 错误表现出来。可能是functools 或者可能是map 这不起作用,我没有看到在封闭的函数内遇到断点。
  • 请创建一个MCVE 来演示问题。

标签: python django python-3.x eclipse functools


【解决方案1】:

在 Python 3 中,映射函数 returns an iterator instead of a list

这意味着,如果您对集合调用 map,则调用的效果在您迭代生成的迭代器之前不会具体化。

考虑这个类:

>>> class C:
...     def __init__(self, x):
...         self.x = x
...     def double(self):
...         self.x *= 2
...     def __repr__(self):                                                                                             
...         return '<C:{}>'.format(self.x)
... 

让我们列出实例:

>>> cs = [C(x) for x in range(1, 4)]
>>> cs
[<C:1>, <C:2>, <C:3>]

现在使用map调用每个实例的double方法:

>>> res = map(C.double, cs)

注意结果不是列表:

>>> res
<map object at 0x7ff276350470>

并且实例没有改变:

>>> cs
[<C:1>, <C:2>, <C:3>]

如果我们在迭代器上调用 next,实例会依次更新。

>>> next(res)
>>> cs
[<C:2>, <C:2>, <C:3>]
>>> next(res)
>>> cs
[<C:2>, <C:4>, <C:3>]
>>> next(res)
>>> cs
[<C:2>, <C:4>, <C:6>]

在您提供的代码示例中,调用map 的结果未分配给变量,因此map 用于其副作用而不是其输出。在 Python 3 中,正确的做法是遍历可迭代对象并在每个元素上调用函数:

>>> for c in cs:
        c.double()

正如链接的文档所说:

特别棘手的是 map() 调用函数的副作用;正确的转换是使用常规的 for 循环(因为创建列表会很浪费)。

【讨论】:

  • 所以他们实际上不鼓励使用map() 以这种方式产生副作用,看起来?是否有替代它(在functools 或其他地方),它返回不需要将它包装在列表中或做一些其他杂技来实现列表的列表(例如,对于可迭代的有界集合)?
猜你喜欢
  • 1970-01-01
  • 2015-03-15
  • 2018-01-14
  • 2019-04-29
  • 2014-12-16
  • 2020-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多