[英]Subclassing a class with __slots__
I'm trying to subclass Scrapy's XPathSelector
and patch in support for CSS3 selectors. 我正在尝试将Scrapy的
XPathSelector
和补丁子类化,以支持CSS3选择器。
XPathSelector
is defined like this: XPathSelector
的定义如下:
class XPathSelector(object_ref):
__slots__ = ['doc', 'xmlNode', 'expr', '__weakref__']
def __init__(self, response=None, text=None, node=None, parent=None, expr=None):
if parent is not None:
self.doc = parent.doc
...
I subclass XPathSelector
and override __init__
: 我将
XPathSelector
子类XPathSelector
并覆盖__init__
:
class CSSSelector(XPathSelector):
def __init__(self, *args, **kwargs):
translator = kwargs.get('translator', 'html').lower()
if 'translator' in kwargs:
del kwargs['translator']
super(XPathSelector, self).__init__(*args, **kwargs)
When I try to use CSSSelector
, I get AttributeError
s errors for doc
, xmlNode
and expr
. 当我尝试使用
CSSSelector
,我得到了doc
, xmlNode
和expr
AttributeError
错误。 Manually adding in those slots into CSSSelector
doesn't help either. 手动将这些插槽添加到
CSSSelector
也无济于事。
What is the proper way to subclass a class with __slot__
s? 使用
__slot__
s对类进行子类化的正确方法是什么?
My complete code is here: 我的完整代码在这里:
"""
Extends `XPathSelector` to allow CSS3 selectors via the `cssselect` library.
"""
from cssselect import HTMLTranslator, GenericTranslator
from scrapy.selector import XPathSelector, XPathSelectorList
__all__ = ['CSSSelector', 'CSSSelectorList']
class CSSSelector(XPathSelector):
__slots__ = ['doc', 'xmlNode', 'expr', 'translator']
def __init__(self, *args, **kwargs):
translator = kwargs.get('translator', 'html').lower()
if 'translator' in kwargs:
del kwargs['translator']
super(CSSSelector, self).__init__(*args, **kwargs)
if translator == 'html':
self.translator = HTMLTranslator()
elif translator == 'xhtml':
self.translator = HTMLTranslator(xhtml=True)
elif translator == 'xml':
self.translator = GenericTranslator()
else:
raise ValueError("Invalid translator: %s. Valid translators are 'html' (default), 'xhtml' and 'xml'." % translator)
def _select_xpath(self, xpath):
if hasattr(self.xmlNode, 'xpathEval'):
self.doc.xpathContext.setContextNode(self.xmlNode)
xpath = unicode_to_str(xpath, 'utf-8')
try:
xpath_result = self.doc.xpathContext.xpathEval(xpath)
except libxml2.xpathError:
raise ValueError("Invalid XPath: %s" % xpath)
if hasattr(xpath_result, '__iter__'):
return CSSSelectorList([self.__class__(node=node, parent=self, expr=xpath) for node in xpath_result])
else:
return CSSSelectorList([self.__class__(node=xpath_result, parent=self, expr=xpath)])
else:
return CSSSelectorList([])
def select(self, selector):
xpath = self.translator.css_to_xpath(selector)
return self._select_xpath(xpath)
def attribute(self, name):
return self._select_xpath('self::@' + name)
def text(self):
return self._select_xpath('self::text()')
class CSSSelectorList(XPathSelectorList):
def attribute(self, name):
return [x.attribute(name) for x in self]
def text(self, name):
return [x.text() for x in self]
I can initialize the class just fine: 我可以很好地初始化这个类:
>>> css_selector = CSSSelector(response)
But I get AttributeError
s all over the place: 但我到处都得到了
AttributeError
:
>>> css_selector.select('title')
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-150-d21b0f17d4cc> in <module>()
----> 1 css_selector.select('title')
<ipython-input-147-c855c7eaf9fa> in select(self, selector)
57
58
---> 59 return self._select_xpath(xpath)
60
61
<ipython-input-147-c855c7eaf9fa> in _select_xpath(self, xpath)
34
35 def _select_xpath(self, xpath):
---> 36 if hasattr(self.xmlNode, 'xpathEval'):
37 self.doc.xpathContext.setContextNode(self.xmlNode)
38 xpath = unicode_to_str(xpath, 'utf-8')
AttributeError: xmlNode
There is nothing wrong with using __slots__
. 使用
__slots__
没有任何问题。 The problem is that you don't call __init__
of XPathSelector
from subclass. 问题是你没有从子类调用
XPathSelector
__init__
。
Instead of super(XPathSelector, self)
there should be super(CSSSelector, self)
: 而不是
super(XPathSelector, self)
应该有super(CSSSelector, self)
:
class CSSSelector(XPathSelector):
def __init__(self, *args, **kwargs):
# ...
super(CSSSelector, self).__init__(*args, **kwargs)
See a good topic on super in Python: Understanding Python super() with __init__() methods . 在Python中查看关于super的一个好主题: 用__init __()方法理解Python super() 。
For what it's worth, if you extend a class with __slots__
, generally you should add __slots__
in a subclass too, at least an empty one. 对于它的价值,如果你用
__slots__
扩展一个类,通常你应该在子类中添加__slots__
,至少是一个空的。 Otherwise, a per-instance dictionary will be created anyway, making __slots__
of base class effectively useless. 否则,无论如何都会创建一个实例字典,使得基类的
__slots__
实际上无用。 From Python Reference : 来自Python参考 :
The action of a
__slots__
declaration is limited to the class where it is defined.__slots__
声明的操作仅限于定义它的类。 As a result, subclasses will have a__dict__
unless they also define__slots__
(which must only contain names of any additional slots).因此,子类将具有
__dict__
除非它们还定义__slots__
(它必须只包含任何其他插槽的名称)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.