如何覆盖在python中以两个下划线开头的函数?

时间:2016-03-08 10:44:55

标签: python python-2.7 openerp-7

我使用Openerp 7.0,我想覆盖定义如下的函数__compute:

class account_account(osv.osv):
    _name = "account.account"
    _description = "Account"

    def __compute(self, cr, uid, ids, field_names, arg=None, context=None,
                  query='', query_params=()):
        """ compute the balance, debit and/or credit for the provided
        account ids
        Arguments:
        `ids`: account ids
        `field_names`: the fields to compute (a list of any of
                       'balance', 'debit' and 'credit')
        `arg`: unused fields.function stuff
        `query`: additional query filter (as a string)
        `query_params`: parameters for the provided query string
                        (__compute will handle their escaping) as a
                        tuple
        """
        mapping = {
            'balance': "COALESCE(SUM(l.debit),0) - COALESCE(SUM(l.credit), 0) as balance",
            'debit': "COALESCE(SUM(l.debit), 0) as debit",
            'credit': "COALESCE(SUM(l.credit), 0) as credit",
            # by convention, foreign_balance is 0 when the account has no secondary currency, because the amounts may be in different currencies
            'foreign_balance': "(SELECT CASE WHEN currency_id IS NULL THEN 0 ELSE COALESCE(SUM(l.amount_currency), 0) END FROM account_account WHERE id IN (l.account_id)) as foreign_balance",
        }
        #get all the necessary accounts
        children_and_consolidated = self._get_children_and_consol(cr, uid, ids, context=context)
        #compute for each account the balance/debit/credit from the move lines
        accounts = {}
        res = {}
        null_result = dict((fn, 0.0) for fn in field_names)
        if children_and_consolidated:
            aml_query = self.pool.get('account.move.line')._query_get(cr, uid, context=context)

            wheres = [""]
            if query.strip():
                wheres.append(query.strip())
            if aml_query.strip():
                wheres.append(aml_query.strip())
            filters = " AND ".join(wheres)
            # IN might not work ideally in case there are too many
            # children_and_consolidated, in that case join on a
            # values() e.g.:
            # SELECT l.account_id as id FROM account_move_line l
            # INNER JOIN (VALUES (id1), (id2), (id3), ...) AS tmp (id)
            # ON l.account_id = tmp.id
            # or make _get_children_and_consol return a query and join on that
            request = ("SELECT l.account_id as id, " +\
                       ', '.join(mapping.values()) +
                       " FROM account_move_line l" \
                       " WHERE l.account_id IN %s " \
                            + filters +
                       " GROUP BY l.account_id")
            params = (tuple(children_and_consolidated),) + query_params
            cr.execute(request, params)

            for row in cr.dictfetchall():
                accounts[row['id']] = row

            # consolidate accounts with direct children
            children_and_consolidated.reverse()
            brs = list(self.browse(cr, uid, children_and_consolidated, context=context))
            sums = {}
            currency_obj = self.pool.get('res.currency')
            while brs:
                current = brs.pop(0)

                for fn in field_names:
                    sums.setdefault(current.id, {})[fn] = accounts.get(current.id, {}).get(fn, 0.0)
                    for child in current.child_id:
                        if not child.active:
                            continue
                        if child.company_id.currency_id.id == current.company_id.currency_id.id:
                            try:
                                sums[current.id][fn] += sums[child.id][fn]
                            except:
                                sums[current.id][fn] += accounts.get(child.id, {}).get(fn, 0.0)
                        else:
                            sums[current.id][fn] += currency_obj.compute(cr, uid, child.company_id.currency_id.id, current.company_id.currency_id.id, sums[child.id][fn], context=context)

                # as we have to relay on values computed before this is calculated separately than previous fields
                if current.currency_id and current.exchange_rate and \
                            ('adjusted_balance' in field_names or 'unrealized_gain_loss' in field_names):
                    # Computing Adjusted Balance and Unrealized Gains and losses
                    # Adjusted Balance = Foreign Balance / Exchange Rate
                    # Unrealized Gains and losses = Adjusted Balance - Balance
                    adj_bal = sums[current.id].get('foreign_balance', 0.0) / current.exchange_rate
                    sums[current.id].update({'adjusted_balance': adj_bal, 'unrealized_gain_loss': adj_bal - sums[current.id].get('balance', 0.0)})
            for id in ids:
                res[id] = sums.get(id, null_result)
        else:
            for id in ids:
                res[id] = null_result
        return res

    account_account()

我读到当你想要覆盖带有双下划线的函数时,你在方法名称前加一个下划线,并将定义的类名称作为以下内容:

def _account_account__compute(self, cr, uid, ids, field_names, arg=None, context=None,
              query='', query_params=()):

但它不起作用。

2 个答案:

答案 0 :(得分:2)

您阅读的内容是由于name mangling,双下划线前缀方法变为私有。此过程重写类定义中的名称以指向新名称。新名称按照您的说法构建:_<class name><class method name>。请考虑以下示例:

class A():
  def public(self): print('public() called')
  def _internal_use(self): print('_internal_use() called')
  def __private(self): print('__private() called')
  def f(self): self.__private()

现在让我们看看A.__dict__,它是按名称存储方法的结构:

>>> A.__dict__
mappingproxy({
  'f': <function A.f at 0x1028908c8>,
  '_A__private': <function A.__private at 0x1028906a8>,
  '_internal_use': <function A._internal_use at 0x102890620>,
  '__weakref__': <attribute '__weakref__' of 'A' objects>,
  '__dict__': <attribute '__dict__' of 'A' objects>,
  '__module__': '__main__',
  '__doc__': None,
  'public': <function A.public at 0x102890598>
})

除其他外,请注意您有_A__private_internal_usepublic

请注意,这些名称在模块范围内不存在,它们仅存在于类的__dict__内。当Python解析成员访问时,它会查看对象的__dict__内部。如果不是fount,它会调查班级&#39; __dict__和超级班级&#39; __dict__

a = A()
a.public
# Python looks into a.__dict__. If not found, it looks into type(a).__dict__

这样您就可以访问public_internal_use,但找不到__private,因为该名称甚至不存在。您可以访问的是_A__private

a.public        # works
a.f             # works
a._internal_use # works
a.__private     # AttributeError!
a._A__private   # works again

请注意,这两个名称都没有在模块全局范围中定义:

public         # NameError!
_internal_use  # NameError!
__private      # NameError!
_A__private    # NameError!

但是您尝试通过简单地定义具有该名称的模块函数来覆盖该函数。好吧,Python成员访问解析永远不会涉及全局范围。所以你有几个选择:

  1. 您可以创建另一个继承类并重新定义该函数:

    class B(A):
      def _A__private(self): print('New __private() called')
    
      a = A()
      a.f()   # prints __private() called
      b = B()
      b.f()   # prints New __private() called
    
  2. 您可以直接使用任何其他函数(甚至是lambda)覆盖该方法:

    A._A__private = lambda self: print('This is a lambda')
    a = A()
    a.f()   # prints This is a lambda
    

答案 1 :(得分:0)

您需要创建继承自odoo类的account_account类。然后,您可以使用super():

覆盖方法
class stock_move(osv.osv):
    _inherit = "account.account"

    # new columns
    # ...
    def __compute(self, cr, uid, ids, field_names, arg=None, context=None,
              query='', query_params=()):
        # do your stuff
        # call parent method if needed
        return super(stock_move, self).__compute(self, cr, uid, ids, field_names, arg=None, context=None, query='', query_params=())

这是一个示例,覆盖一个非常常见的create方法,因为您可能希望在创建对象时添加一些额外的功能:

class mrp_product_produce(osv.osv_memory):

    _inherit = "mrp.product.produce"  

    def create(self, cr, uid, vals, context=None):
        res ={}
        res = super(product_product, self).create(cr, uid, vals, context)
        if 'supply_method' in vals and vals['supply_method'] == 'produce':
            sequence_type_obj = self.pool.get('ir.sequence.type')
            id_type = sequence_type_obj.create(cr, uid, {'name': vals['name'], 'code': 'product_produce_' + str(res)})
            sequence_obj = self.pool.get('ir.sequence')
            sequence_obj.create(cr, uid, {'name': vals['name'], 'code': 'product_produce_' + str(res), 'padding': 7, 'number_increment': 1, 'number_next': 1 })
        return res