当Category遇到Inherit

本文探讨了Objective-C中类别(Category)与子类方法的调用顺序及覆盖规则,揭示了如何通过子类实例调用方法时的具体行为。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们知道在Category可以给动态的给一个类添加方法实现,这样我们可以很方便的把一个大类拆分成很多个子模块,而且Category里面的方法会覆盖基本类里面同名的方法。然而有一种特殊情况,当Category和子类中,同时覆盖了基本类里面的方法的时候,通过子类的实例对象调用该方法,实际上会调用哪一个方法呢?实际上,在OC中,一个类的加载顺序是这样的,加载基类中的方法->加载Category中的方法->加载子类的方法。所以我们通过子类的实例对象调用的其实是子类自己的方法!

新建知识,点击保存草稿后提示错误,错误如下 TypeError: BaseModel.search() takes from 2 to 5 positional arguments but 6 were given 完整代码如下: class DocumentPage(models.Model): """This class is use to manage Document.""" _name = "document.page" _inherit = ["mail.thread", "mail.activity.mixin"] _description = "Document Page" _order = "name" _HTML_WIDGET_DEFAULT_VALUE = "<p><br></p>" name = fields.Char("Title", required=True) type = fields.Selection( [("content", "Content"), ("category", "Category")], help="Page type", default="content", ) active = fields.Boolean(default=True) parent_id = fields.Many2one( "document.page", "Category", domain=[("type", "=", "category")] ) child_ids = fields.One2many("document.page", "parent_id", "Children") content = fields.Html( compute="_compute_content", inverse="_inverse_content", search="_search_content", sanitize=False, ) draft_name = fields.Char( string="Name", help="Name for the changes made", related="history_head.name", readonly=False, ) draft_summary = fields.Char( string="Summary", help="Describe the changes made", related="history_head.summary", readonly=False, ) template = fields.Html( help="Template that will be used as a content template " "for all new page of this category.", ) history_head = fields.Many2one( "document.page.history", "HEAD", compute="_compute_history_head", store=True, auto_join=True, ) history_ids = fields.One2many( "document.page.history", "page_id", "History", readonly=True, ) menu_id = fields.Many2one("ir.ui.menu", "Menu", readonly=True) content_date = fields.Datetime( "Last Contribution Date", related="history_head.create_date", store=True, index=True, readonly=True, ) content_uid = fields.Many2one( "res.users", "Last Contributor", related="history_head.create_uid", store=True, index=True, readonly=True, ) company_id = fields.Many2one( "res.company", "Company", help="If set, page is accessible only from this company", index=True, ondelete="cascade", default=lambda self: self.env.company, ) backend_url = fields.Char( string="Backend URL", help="Use it to link resources univocally", compute="_compute_backend_url", ) state = fields.Selection( selection=[ ('draft', '草稿'), ('under_review', '审核中'), ('approved', '已通过'), ('rejected', '已驳回') ], string='状态', default='draft', tracking=True, # 关键修复:启用邮件跟踪 index=True ) visible_department_ids = fields.Many2many( 'hr.department', string='可见部门', required=True, tracking=True ) create_uid = fields.Many2one( 'res.users', string='创建人', index=True # 直接在此处添加索引 ) department_manager_ids = fields.Many2many( 'res.users', string='部门管理员', compute='_compute_department_managers', store=False, # 不存储,实时计算 help="自动关联可见部门的管理员用户", index=True ) @api.depends("menu_id", "parent_id.menu_id") def _compute_backend_url(self): tmpl = "/web#id={}&model=document.page&view_type=form" for rec in self: url = tmpl.format(rec.id) # retrieve action action = None parent = rec while not action and parent: action = parent.menu_id.action parent = parent.parent_id if action: url += f"&action={action.id}" rec.backend_url = url @api.constrains("parent_id") def _check_parent_id(self): if not self._check_recursion(): raise ValidationError(_("You cannot create recursive categories.")) def _get_page_index(self, link=True): """Return the index of a document.""" self.ensure_one() index = [ "<li>" + subpage._get_page_index() + "</li>" for subpage in self.child_ids ] r = "" if link: r = f'<a href="{self.backend_url}">{self.name}</a>' if index: r += "<ul>" + "".join(index) + "</ul>" return r @api.depends("history_head") def _compute_content(self): for rec in self: if rec.type == "category": rec.content = rec._get_page_index(link=False) else: if rec.history_head: rec.content = rec.history_head.content else: # html widget's default, so it doesn't trigger ghost save rec.content = self._HTML_WIDGET_DEFAULT_VALUE def _inverse_content(self): for rec in self: if rec.type == "content" and rec.content != rec.history_head.content: rec._create_history( { "page_id": rec.id, "name": rec.draft_name, "summary": rec.draft_summary, "content": rec.content, } ) def _create_history(self, vals): self.ensure_one() return self.env["document.page.history"].create(vals) def _search_content(self, operator, value): return [("history_head.content", operator, value)] @api.depends("history_ids") def _compute_history_head(self): for rec in self: if rec.history_ids: rec.history_head = rec.history_ids[0] else: rec.history_head = False @api.onchange("parent_id") def _onchange_parent_id(self): """We Set it the right content to the new parent.""" if ( self.content in (False, self._HTML_WIDGET_DEFAULT_VALUE) and self.parent_id.type == "category" ): self.content = self.parent_id.template def unlink(self): menus = self.mapped("menu_id") res = super().unlink() menus.unlink() return res def copy(self, default=None): default = dict( default or {}, name=_("%s (copy)") % self.name, content=self.content, draft_name="1.0", draft_summary=_("summary"), ) return super().copy(default=default) def _compute_department_managers(self): for record in self: record.department_manager_ids = record.visible_department_ids.mapped('manager_id.user_id') @api.model def search(self, args, offset=0, limit=None, order=None, count=False): """修正参数数量的搜索方法""" user = self.env.user # 管理员豁免过滤 if not user.has_group('base.group_system'): security_condition = [ '|', # 允许以下任一条件 ('state', 'not in', ['draft', 'under_review']), '|', '&', ('state', '=', 'draft'), ('create_uid', '=', user.id), '&', ('state', '=', 'under_review'), '|', ('create_uid', '=', user.id), ('visible_department_ids.manager_id.user_id', '=', user.id) ] args = expression.AND([args, security_condition]) # 确保只传递5个参数 return super(DocumentPage, self).search(args, offset, limit, order, count) def check_access_rule(self, operation): """调整后的权限验证(仅处理直接访问)""" user = self.env.user try: # 执行父类基础验证 super(DocumentPage, self).check_access_rule(operation) except AccessError: # 仅在直接访问受限文档时抛出错误 for rec in self: if rec.state == 'draft' and rec.create_uid != user: raise AccessError(_("您没有权限查看此草稿文档")) if rec.state == 'under_review': is_manager = user.id in rec.visible_department_ids.mapped('manager_id.user_id.id') if not is_manager and rec.create_uid != user: raise AccessError(_("需要部门管理员权限"))
最新发布
05-14
### 反向传播中的继承概念 在神经网络训练过程中,反向传播算法用于计算损失函数相对于各个权重的梯度。这些梯度随后被用来更新模型参数以最小化误差。然而,“继承”这一术语通常不直接应用于反向传播过程本身。 更确切地说,在构建复杂的神经网络架构时,模块化的编程方式允许创建可重用组件,即所谓的“Module”。每个`Module`可以视为独立的小型神经网络,并且可以通过组合多个这样的模块来形成更大规模的网络结构[^2]。当定义新的层或操作作为现有类的一个子类时,则实现了面向对象意义上的继承机制。 对于具体的实现而言,假设有一个基础卷积层 `ConvLayerBase` ,它包含了基本属性方法: ```python import torch.nn as nn class ConvLayerBase(nn.Module): def __init__(self, in_channels, out_channels, kernel_size=3): super(ConvLayerBase, self).__init__() self.conv = nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=kernel_size) def forward(self, x): return self.conv(x) ``` 现在如果希望基于这个基底进一步扩展功能,比如加入批标准化处理(Batch Normalization),就可以通过继承的方式轻松做到这一点: ```python class ExtendedConvLayer(ConvLayerBase): def __init__(self, *args, **kwargs): super(ExtendedConvLayer, self).__init__(*args, **kwargs) self.bn = nn.BatchNorm2d(num_features=self.out_channels) def forward(self, x): conv_output = super().forward(x) normalized_output = self.bn(conv_output) return normalized_output ``` 在这个例子中,虽然并没有显式提到反向传播的具体细节,但是每当调用了 `.backward()` 方法执行自动微分运算时,PyTorch框架会自动应用链式法则遍历整个计算图并累积相应的梯度值给每一个参与前馈传递路径上的张量变量。因此可以说这种设计模式间接支持了所谓 “通过反向传播进行继承”的理念——新特性可以在不影响原有逻辑的基础上无缝融入到既有体系当中去。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值