How to make a check like in kivy?
I don’t understand how to do assignment checking to a variable, as it is done in Kivy.
I know how this is done for class properties, and it looks like this
#!/usr/bin/python3.6
class Foo:
var = property()
def __init__(self):
self._var = 0
@var.setter
def var(self, value):
self._var = value
# code setter
pass
@var.getter
def var(self):
# code getter
print('Getter method')
return self._var
a = Foo()
a.var = 5
print(a.var)
# Getter method
# 5
In Kivy can do:
class LabelBase(Label):
msg = StringProperty('t')
def __init__(self, **kwargs):
super(LabelBase, self).__init__(**kwargs)
self.msg = 5
I take
Traceback (most recent call last):
File "/home/Python/Prj/View/Main.py", line 83, in <module>
Start().build()
File "/home/Python/Prj/View/Main.py", line 73, in build
GUI().run()
File "/usr/lib/python3/dist-packages/kivy/app.py", line 800, in run
root = self.build()
File "/home/Python/Prj/View/Main.py", line 65, in build
main_window = MainFrame()
File "/home/Python/Prj/View/Main.py", line 52, in __init__
self.label = LabelBase(text='test')
File "/home/Python/Prj/View/Main.py", line 16, in __init__
self.msg = 5
File "kivy/properties.pyx", line 483, in
kivy.properties.Property.__set__
File "kivy/properties.pyx", line 521, in kivy.properties.Property.set
File "kivy/properties.pyx", line 512, in kivy.properties.Property.set
File "kivy/properties.pyx", line 678, in
kivy.properties.StringProperty.check
ValueError: LabelBase.msg accept only str
Renamed the question because it did not correspond to what was happening
python kivy
|
show 15 more comments
I don’t understand how to do assignment checking to a variable, as it is done in Kivy.
I know how this is done for class properties, and it looks like this
#!/usr/bin/python3.6
class Foo:
var = property()
def __init__(self):
self._var = 0
@var.setter
def var(self, value):
self._var = value
# code setter
pass
@var.getter
def var(self):
# code getter
print('Getter method')
return self._var
a = Foo()
a.var = 5
print(a.var)
# Getter method
# 5
In Kivy can do:
class LabelBase(Label):
msg = StringProperty('t')
def __init__(self, **kwargs):
super(LabelBase, self).__init__(**kwargs)
self.msg = 5
I take
Traceback (most recent call last):
File "/home/Python/Prj/View/Main.py", line 83, in <module>
Start().build()
File "/home/Python/Prj/View/Main.py", line 73, in build
GUI().run()
File "/usr/lib/python3/dist-packages/kivy/app.py", line 800, in run
root = self.build()
File "/home/Python/Prj/View/Main.py", line 65, in build
main_window = MainFrame()
File "/home/Python/Prj/View/Main.py", line 52, in __init__
self.label = LabelBase(text='test')
File "/home/Python/Prj/View/Main.py", line 16, in __init__
self.msg = 5
File "kivy/properties.pyx", line 483, in
kivy.properties.Property.__set__
File "kivy/properties.pyx", line 521, in kivy.properties.Property.set
File "kivy/properties.pyx", line 512, in kivy.properties.Property.set
File "kivy/properties.pyx", line 678, in
kivy.properties.StringProperty.check
ValueError: LabelBase.msg accept only str
Renamed the question because it did not correspond to what was happening
python kivy
1
Assignment isn't a method.
– PM 2Ring
Nov 23 '18 at 8:32
Good, how was it done team Kivy?
– Shatten
Nov 23 '18 at 8:34
1
I doubt very much that this is possible even in Kivy. Please show an example where you do that.
– Daniel Roseman
Nov 23 '18 at 8:36
2
But isn't that exactly the same as what you have in your first snippet above, with getter and setter methods? It's not at all what you do in the second, where you try and reassign the whole object.
– Daniel Roseman
Nov 23 '18 at 8:41
1
Okay after reading the source code I understand what they're doing, so what is it exactly that you want? Because what kivy is doing and what you're asking to do (claiming that kivy does it) are completely different.
– SilverSlash
Nov 23 '18 at 9:44
|
show 15 more comments
I don’t understand how to do assignment checking to a variable, as it is done in Kivy.
I know how this is done for class properties, and it looks like this
#!/usr/bin/python3.6
class Foo:
var = property()
def __init__(self):
self._var = 0
@var.setter
def var(self, value):
self._var = value
# code setter
pass
@var.getter
def var(self):
# code getter
print('Getter method')
return self._var
a = Foo()
a.var = 5
print(a.var)
# Getter method
# 5
In Kivy can do:
class LabelBase(Label):
msg = StringProperty('t')
def __init__(self, **kwargs):
super(LabelBase, self).__init__(**kwargs)
self.msg = 5
I take
Traceback (most recent call last):
File "/home/Python/Prj/View/Main.py", line 83, in <module>
Start().build()
File "/home/Python/Prj/View/Main.py", line 73, in build
GUI().run()
File "/usr/lib/python3/dist-packages/kivy/app.py", line 800, in run
root = self.build()
File "/home/Python/Prj/View/Main.py", line 65, in build
main_window = MainFrame()
File "/home/Python/Prj/View/Main.py", line 52, in __init__
self.label = LabelBase(text='test')
File "/home/Python/Prj/View/Main.py", line 16, in __init__
self.msg = 5
File "kivy/properties.pyx", line 483, in
kivy.properties.Property.__set__
File "kivy/properties.pyx", line 521, in kivy.properties.Property.set
File "kivy/properties.pyx", line 512, in kivy.properties.Property.set
File "kivy/properties.pyx", line 678, in
kivy.properties.StringProperty.check
ValueError: LabelBase.msg accept only str
Renamed the question because it did not correspond to what was happening
python kivy
I don’t understand how to do assignment checking to a variable, as it is done in Kivy.
I know how this is done for class properties, and it looks like this
#!/usr/bin/python3.6
class Foo:
var = property()
def __init__(self):
self._var = 0
@var.setter
def var(self, value):
self._var = value
# code setter
pass
@var.getter
def var(self):
# code getter
print('Getter method')
return self._var
a = Foo()
a.var = 5
print(a.var)
# Getter method
# 5
In Kivy can do:
class LabelBase(Label):
msg = StringProperty('t')
def __init__(self, **kwargs):
super(LabelBase, self).__init__(**kwargs)
self.msg = 5
I take
Traceback (most recent call last):
File "/home/Python/Prj/View/Main.py", line 83, in <module>
Start().build()
File "/home/Python/Prj/View/Main.py", line 73, in build
GUI().run()
File "/usr/lib/python3/dist-packages/kivy/app.py", line 800, in run
root = self.build()
File "/home/Python/Prj/View/Main.py", line 65, in build
main_window = MainFrame()
File "/home/Python/Prj/View/Main.py", line 52, in __init__
self.label = LabelBase(text='test')
File "/home/Python/Prj/View/Main.py", line 16, in __init__
self.msg = 5
File "kivy/properties.pyx", line 483, in
kivy.properties.Property.__set__
File "kivy/properties.pyx", line 521, in kivy.properties.Property.set
File "kivy/properties.pyx", line 512, in kivy.properties.Property.set
File "kivy/properties.pyx", line 678, in
kivy.properties.StringProperty.check
ValueError: LabelBase.msg accept only str
Renamed the question because it did not correspond to what was happening
python kivy
python kivy
edited Nov 23 '18 at 11:02
asked Nov 23 '18 at 8:26
Shatten
33
33
1
Assignment isn't a method.
– PM 2Ring
Nov 23 '18 at 8:32
Good, how was it done team Kivy?
– Shatten
Nov 23 '18 at 8:34
1
I doubt very much that this is possible even in Kivy. Please show an example where you do that.
– Daniel Roseman
Nov 23 '18 at 8:36
2
But isn't that exactly the same as what you have in your first snippet above, with getter and setter methods? It's not at all what you do in the second, where you try and reassign the whole object.
– Daniel Roseman
Nov 23 '18 at 8:41
1
Okay after reading the source code I understand what they're doing, so what is it exactly that you want? Because what kivy is doing and what you're asking to do (claiming that kivy does it) are completely different.
– SilverSlash
Nov 23 '18 at 9:44
|
show 15 more comments
1
Assignment isn't a method.
– PM 2Ring
Nov 23 '18 at 8:32
Good, how was it done team Kivy?
– Shatten
Nov 23 '18 at 8:34
1
I doubt very much that this is possible even in Kivy. Please show an example where you do that.
– Daniel Roseman
Nov 23 '18 at 8:36
2
But isn't that exactly the same as what you have in your first snippet above, with getter and setter methods? It's not at all what you do in the second, where you try and reassign the whole object.
– Daniel Roseman
Nov 23 '18 at 8:41
1
Okay after reading the source code I understand what they're doing, so what is it exactly that you want? Because what kivy is doing and what you're asking to do (claiming that kivy does it) are completely different.
– SilverSlash
Nov 23 '18 at 9:44
1
1
Assignment isn't a method.
– PM 2Ring
Nov 23 '18 at 8:32
Assignment isn't a method.
– PM 2Ring
Nov 23 '18 at 8:32
Good, how was it done team Kivy?
– Shatten
Nov 23 '18 at 8:34
Good, how was it done team Kivy?
– Shatten
Nov 23 '18 at 8:34
1
1
I doubt very much that this is possible even in Kivy. Please show an example where you do that.
– Daniel Roseman
Nov 23 '18 at 8:36
I doubt very much that this is possible even in Kivy. Please show an example where you do that.
– Daniel Roseman
Nov 23 '18 at 8:36
2
2
But isn't that exactly the same as what you have in your first snippet above, with getter and setter methods? It's not at all what you do in the second, where you try and reassign the whole object.
– Daniel Roseman
Nov 23 '18 at 8:41
But isn't that exactly the same as what you have in your first snippet above, with getter and setter methods? It's not at all what you do in the second, where you try and reassign the whole object.
– Daniel Roseman
Nov 23 '18 at 8:41
1
1
Okay after reading the source code I understand what they're doing, so what is it exactly that you want? Because what kivy is doing and what you're asking to do (claiming that kivy does it) are completely different.
– SilverSlash
Nov 23 '18 at 9:44
Okay after reading the source code I understand what they're doing, so what is it exactly that you want? Because what kivy is doing and what you're asking to do (claiming that kivy does it) are completely different.
– SilverSlash
Nov 23 '18 at 9:44
|
show 15 more comments
2 Answers
2
active
oldest
votes
I assume you want to do some checks on assignments, like type checking in the kivy case. Your example looks to me very unidiomatic, I'd write it the following way:
class Foo:
@property
def var(self):
return self._var
@var.setter
def var(self, value):
if type(value) == str:
self._var = value
How does it work? Actually it's a bit complicated, but here is a good ressource. property
returns a descriptor which basically means that foo.var = x
is translated to foo.var.__set__(x)
(which then calls something "equivalent" to Foo.var.setter_function(foo, x)
). It just says "instead of storing an assigned value, call this function".
How is the kivy case different? Assume we have the following class:
class Bar(Widget):
var = StringProperty()
The behavior is very similar to the python code before, but the setter and getter methods are defined by kivy and not here in the class. But if you assign a value to a Bar instance bar.var = x
a setter is called bar.var.__set__(x)
. The setter does not only check for types but also emits events if the value changed.
You can also create properties with getters and setters already provided by implementing a descriptor. You need to implement __set__
and __get__
:
class MyStringProperty:
def __init__(self, default):
self._default = default
def __set__(self, instance, value):
if type(value) == str:
instance._value = value
def __get__(self, instance, owner):
return getattr(instance, "_value", self._default)
class Foo:
var = MyStringProperty("x")
foo = Foo()
print(foo.var)
foo.var = 3
print(foo.var)
foo.var = "asdf"
print(foo.var)
(The documentation speaks from get, set, delete and set_name, but I think in the common case one can get away with only implementing set and get.)
I'm understood, thank you!
– Shatten
Nov 26 '18 at 3:34
add a comment |
If you want to check the value, you simply add check code to your setter
. For example, if you want var
to be limited to int
or float
, you can modify your setter
as:
@var.setter
def var(self, value):
if type(value) not in (int, float):
raise ValueError('%s.%s accept only int or float (got %r)' % (
self.__class__.__name__,
'var', value))
self._var = value
This code is a slight modification of Kivy
check code.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53442997%2fhow-to-make-a-check-like-in-kivy%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
I assume you want to do some checks on assignments, like type checking in the kivy case. Your example looks to me very unidiomatic, I'd write it the following way:
class Foo:
@property
def var(self):
return self._var
@var.setter
def var(self, value):
if type(value) == str:
self._var = value
How does it work? Actually it's a bit complicated, but here is a good ressource. property
returns a descriptor which basically means that foo.var = x
is translated to foo.var.__set__(x)
(which then calls something "equivalent" to Foo.var.setter_function(foo, x)
). It just says "instead of storing an assigned value, call this function".
How is the kivy case different? Assume we have the following class:
class Bar(Widget):
var = StringProperty()
The behavior is very similar to the python code before, but the setter and getter methods are defined by kivy and not here in the class. But if you assign a value to a Bar instance bar.var = x
a setter is called bar.var.__set__(x)
. The setter does not only check for types but also emits events if the value changed.
You can also create properties with getters and setters already provided by implementing a descriptor. You need to implement __set__
and __get__
:
class MyStringProperty:
def __init__(self, default):
self._default = default
def __set__(self, instance, value):
if type(value) == str:
instance._value = value
def __get__(self, instance, owner):
return getattr(instance, "_value", self._default)
class Foo:
var = MyStringProperty("x")
foo = Foo()
print(foo.var)
foo.var = 3
print(foo.var)
foo.var = "asdf"
print(foo.var)
(The documentation speaks from get, set, delete and set_name, but I think in the common case one can get away with only implementing set and get.)
I'm understood, thank you!
– Shatten
Nov 26 '18 at 3:34
add a comment |
I assume you want to do some checks on assignments, like type checking in the kivy case. Your example looks to me very unidiomatic, I'd write it the following way:
class Foo:
@property
def var(self):
return self._var
@var.setter
def var(self, value):
if type(value) == str:
self._var = value
How does it work? Actually it's a bit complicated, but here is a good ressource. property
returns a descriptor which basically means that foo.var = x
is translated to foo.var.__set__(x)
(which then calls something "equivalent" to Foo.var.setter_function(foo, x)
). It just says "instead of storing an assigned value, call this function".
How is the kivy case different? Assume we have the following class:
class Bar(Widget):
var = StringProperty()
The behavior is very similar to the python code before, but the setter and getter methods are defined by kivy and not here in the class. But if you assign a value to a Bar instance bar.var = x
a setter is called bar.var.__set__(x)
. The setter does not only check for types but also emits events if the value changed.
You can also create properties with getters and setters already provided by implementing a descriptor. You need to implement __set__
and __get__
:
class MyStringProperty:
def __init__(self, default):
self._default = default
def __set__(self, instance, value):
if type(value) == str:
instance._value = value
def __get__(self, instance, owner):
return getattr(instance, "_value", self._default)
class Foo:
var = MyStringProperty("x")
foo = Foo()
print(foo.var)
foo.var = 3
print(foo.var)
foo.var = "asdf"
print(foo.var)
(The documentation speaks from get, set, delete and set_name, but I think in the common case one can get away with only implementing set and get.)
I'm understood, thank you!
– Shatten
Nov 26 '18 at 3:34
add a comment |
I assume you want to do some checks on assignments, like type checking in the kivy case. Your example looks to me very unidiomatic, I'd write it the following way:
class Foo:
@property
def var(self):
return self._var
@var.setter
def var(self, value):
if type(value) == str:
self._var = value
How does it work? Actually it's a bit complicated, but here is a good ressource. property
returns a descriptor which basically means that foo.var = x
is translated to foo.var.__set__(x)
(which then calls something "equivalent" to Foo.var.setter_function(foo, x)
). It just says "instead of storing an assigned value, call this function".
How is the kivy case different? Assume we have the following class:
class Bar(Widget):
var = StringProperty()
The behavior is very similar to the python code before, but the setter and getter methods are defined by kivy and not here in the class. But if you assign a value to a Bar instance bar.var = x
a setter is called bar.var.__set__(x)
. The setter does not only check for types but also emits events if the value changed.
You can also create properties with getters and setters already provided by implementing a descriptor. You need to implement __set__
and __get__
:
class MyStringProperty:
def __init__(self, default):
self._default = default
def __set__(self, instance, value):
if type(value) == str:
instance._value = value
def __get__(self, instance, owner):
return getattr(instance, "_value", self._default)
class Foo:
var = MyStringProperty("x")
foo = Foo()
print(foo.var)
foo.var = 3
print(foo.var)
foo.var = "asdf"
print(foo.var)
(The documentation speaks from get, set, delete and set_name, but I think in the common case one can get away with only implementing set and get.)
I assume you want to do some checks on assignments, like type checking in the kivy case. Your example looks to me very unidiomatic, I'd write it the following way:
class Foo:
@property
def var(self):
return self._var
@var.setter
def var(self, value):
if type(value) == str:
self._var = value
How does it work? Actually it's a bit complicated, but here is a good ressource. property
returns a descriptor which basically means that foo.var = x
is translated to foo.var.__set__(x)
(which then calls something "equivalent" to Foo.var.setter_function(foo, x)
). It just says "instead of storing an assigned value, call this function".
How is the kivy case different? Assume we have the following class:
class Bar(Widget):
var = StringProperty()
The behavior is very similar to the python code before, but the setter and getter methods are defined by kivy and not here in the class. But if you assign a value to a Bar instance bar.var = x
a setter is called bar.var.__set__(x)
. The setter does not only check for types but also emits events if the value changed.
You can also create properties with getters and setters already provided by implementing a descriptor. You need to implement __set__
and __get__
:
class MyStringProperty:
def __init__(self, default):
self._default = default
def __set__(self, instance, value):
if type(value) == str:
instance._value = value
def __get__(self, instance, owner):
return getattr(instance, "_value", self._default)
class Foo:
var = MyStringProperty("x")
foo = Foo()
print(foo.var)
foo.var = 3
print(foo.var)
foo.var = "asdf"
print(foo.var)
(The documentation speaks from get, set, delete and set_name, but I think in the common case one can get away with only implementing set and get.)
answered Nov 23 '18 at 15:43
syntonym
4,43621337
4,43621337
I'm understood, thank you!
– Shatten
Nov 26 '18 at 3:34
add a comment |
I'm understood, thank you!
– Shatten
Nov 26 '18 at 3:34
I'm understood, thank you!
– Shatten
Nov 26 '18 at 3:34
I'm understood, thank you!
– Shatten
Nov 26 '18 at 3:34
add a comment |
If you want to check the value, you simply add check code to your setter
. For example, if you want var
to be limited to int
or float
, you can modify your setter
as:
@var.setter
def var(self, value):
if type(value) not in (int, float):
raise ValueError('%s.%s accept only int or float (got %r)' % (
self.__class__.__name__,
'var', value))
self._var = value
This code is a slight modification of Kivy
check code.
add a comment |
If you want to check the value, you simply add check code to your setter
. For example, if you want var
to be limited to int
or float
, you can modify your setter
as:
@var.setter
def var(self, value):
if type(value) not in (int, float):
raise ValueError('%s.%s accept only int or float (got %r)' % (
self.__class__.__name__,
'var', value))
self._var = value
This code is a slight modification of Kivy
check code.
add a comment |
If you want to check the value, you simply add check code to your setter
. For example, if you want var
to be limited to int
or float
, you can modify your setter
as:
@var.setter
def var(self, value):
if type(value) not in (int, float):
raise ValueError('%s.%s accept only int or float (got %r)' % (
self.__class__.__name__,
'var', value))
self._var = value
This code is a slight modification of Kivy
check code.
If you want to check the value, you simply add check code to your setter
. For example, if you want var
to be limited to int
or float
, you can modify your setter
as:
@var.setter
def var(self, value):
if type(value) not in (int, float):
raise ValueError('%s.%s accept only int or float (got %r)' % (
self.__class__.__name__,
'var', value))
self._var = value
This code is a slight modification of Kivy
check code.
answered Nov 23 '18 at 15:35
John Anderson
2,5131413
2,5131413
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53442997%2fhow-to-make-a-check-like-in-kivy%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
Assignment isn't a method.
– PM 2Ring
Nov 23 '18 at 8:32
Good, how was it done team Kivy?
– Shatten
Nov 23 '18 at 8:34
1
I doubt very much that this is possible even in Kivy. Please show an example where you do that.
– Daniel Roseman
Nov 23 '18 at 8:36
2
But isn't that exactly the same as what you have in your first snippet above, with getter and setter methods? It's not at all what you do in the second, where you try and reassign the whole object.
– Daniel Roseman
Nov 23 '18 at 8:41
1
Okay after reading the source code I understand what they're doing, so what is it exactly that you want? Because what kivy is doing and what you're asking to do (claiming that kivy does it) are completely different.
– SilverSlash
Nov 23 '18 at 9:44