Adding methods in __init__() in Python - methods

I'm making classes which are similar, but with different functions, depending on the use of the class.
class Cup:
def __init__(self, content):
self.content = content
def spill(self):
print(f"The {self.content} was spilled.")
def drink(self):
print(f"You drank the {self.content}.")
Coffee = Cup("coffee")
Coffee.spill()
> The coffee was spilled.
However, it is known during the initialization of an object whether or not the cup will be spilled or drank. If there are many cups, there's no need for all of them to have both functions, because only one of them will be used. How can I add a function during initialization?
Intuitively it should be something like this, but this apparently didn't work:
def spill(self):
print(f"The {self.content} was spilled.")
class Cup:
def __init__(self, content, function):
self.content = content
self.function = function
Coffee = Cup("coffee", spill)
Coffee.function()
> The coffee was spilled

If you create a class in Python with methods e.g.
class A
def method(self, param1, param)
It will make sure that when you call A().method(x,y) it fill the self parameter with instance of A. When you try specify method yourself outside of the class then you have to also make sure that the binding is done properly.
import functools
class Cup:
def __init__(self, content, function):
self.content = content
self.function = functools.partial(function, self)

Related

Can sorbet handle private definitions of initialize() when called with new?

I am getting an error calling the initialize method:
# typed: true
class A
extend T::Sig
sig {params(x: Integer).void}
private def initialize(x)
end
end
def main
A.new(91)
end
Here is the result of sorbet:
editor.rb:11: Non-private call to private method initialize on A https://srb.help/7031
11 | A.new(91)
^^^^^^^^^
editor.rb:6: Defined in A here
6 | private def initialize(x)
^^^^^^^^^^^^^^^^^
Errors: 1
https://sorbet.run/#%23%20typed%3A%20true%0Aclass%20A%0A%20%20extend%20T%3A%3ASig%[…]0A%20%20end%0Aend%0A%0Adef%20main%0A%20%20A.new(91)%20%20%0Aend
This looks like a bug in Sorbet, since initialize is treated specially by the language and is thus always private by default anyway. In other words, your code is 100% identical to the same code without private:
# typed: true
class A
extend T::Sig
sig {params(x: Integer).void}
def initialize(x)
end
end
def main
A.new(91)
end
p A.private_instance_methods(false).include?(:initialize)
#=> true
Which passes type checking with flying colors.
My guess is that Sorbet doesn't "know" about the special treatment of initialize in Ruby and thus treats it as a normal method. My second guess is that Sorbet contains a definition of Class#new that looks like this:
class Class
def new(...)
obj = allocate
obj.initialize(...)
obj
end
end
This means that as long as Sorbet thinks initialize is public, everything is honky-dory, but as soon as it thinks it is private, it fails.
However, the actual implementation of Class#new looks more like this:
class Class
def new(...)
obj = allocate
obj.__send__(:initialize, ...)
obj
end
end
If my assumptions are correct, there are two bugs in Sorbet that normally cancel each other out:
The definition of Class#new is wrong. This would mean that you would always get an error because initialize is always private, except
The default visibility for initialize is also wrong.
However, I have not checked those assumptions, so I could be completely off.

ruby / Passing arguments to a dynamically created class

There's probably many other better ways; but having the following piece of code :
class ApplicationService
def self.build(*args, &block)
new(*args, &block).build
end
end
class BaseClass; end
class Fetcher < ApplicationService
attr_reader :resource_name
def initialize(resource_name)
#resource_name = resource_name
end
def build
resource_name = #resource_name
Class.new(BaseClass) do
##resource_name = resource_name
class << self
def all
"http://some.remote.resource/#{##resource_name}/all"
end
end
end
end
end
in order to have the initial resource_name in the self.all method, i came up with defining ##resource_name = resource_name. I'm totally unsure if that's the good way to go.
I'd like to be able to use such 'generator', in order to provide the following interface :
## In some kind of initializers :
Xyz = Fetcher.build('xyz')
## Final use :
Xyz.all
Would there be a better pattern to have the class created dynamically, while passing arguments when creating this class ?
It is unclear why you want to create the class in the first place. If there are good reasons for it, my answer is kind of invalid.
You can have the desired behaviour using "standard" OOP techniques and working with instances
class Fetcher
def initialize(resource_name)
#resource_name = resource_name
end
def all
"http://some.remote.resource/#{#resource_name}/all"
end
end
xyz_fetcher = Fetcher.new('xyz')
xyz_fetcher.all
Otherwise, your code is more or less what you would/should do, I guess. Just, I would let the Fetcher class act as a singleton (not use an instance of Fetcher):
class Fetcher < ApplicationService
# make a singleton by privatizing initialize (read this up somewhere else)
def self.build(resource_name)
Class.new(BaseClass) do
##resource_name = resource_name
class << self
def all
"http://some.remote.resource/#{##resource_name}/all"
end
end
end
end
end
Then
Xyz = Fetcher.build('xyz')
Xyz.all
Now, you have the stuff with ApplicationService which more or less achieves that (and passes a block), so probably we readers miss some parts of the bigger picture ... please clarify if that is the case.
Besides singletonization, you could also work with modules instead (thanks #max for the comment).

How to make sphinx not ignore the first argument of python class methods?

I'd like to do very basic documentation of my python3 code with sphinx.
An example piece of my code looks like this:
class MyClass:
"""
Class description
"""
def function1(x, y, z):
"""
function description
"""
pass
and the corresponding part in my index.txt file for sphinx like this:
.. automodule:: code.module_name
.. autoclass:: MyClass
:members:
As a result the documentation is otherwise what I'd expect but the function shows as:
function1(y,z)
function description
instead of the desired
function1(x,y,z)
function description
So to put it in short: How to make sphinx not ignore the first argument of python class methods?
I've searched a good while for an answer to this problem and found no answers that would've helped me overcome it. I appreciate any help.
Your function function1 is not a staticmethod, therefore the first arg is self:
class myClass:
def function1(x, y, z):
# x in this case is an alias for self
To get x to show up, either add x as another arg or have the method be static
1:
class myClass:
def function1(self, x, y, z):
# Now x is an explicit var
2:
class myClass
#staticmethod
def function1(x,y,z):
# Now your class does not have access to self, but x is an explicit arg
It will skip self since it is implicitly provided to the function, you should not have to specify it on the method call

Ruby class with public method

I have a class with a public method, for example:
class CsvParse
def initialize(csv_file)
#csv_file = csv_file
end
def parse
...
end
end
csv_parse = CsvParse.new('csv_file.csv')
csv_parse.parse
But the design of the class can be like this too:
class CsvParse
def initialize(csv_file)
#csv_file = csv_file
parse
end
private
def parse
...
end
end
csv_parse = CsvParse.new('csv_file.csv')
What is the best practice?
In this particular case there is a helper method exposed, that parses file to it’s csv representation (whatever it is.)
Semantically the best call would be:
csv_parse = CsvParser.parse 'csv_file.csv'
So, I would go with declaring constructor private, preventing these objects from being directly created:
class CsvParser
def initialize ...
end
private_class_method :new
def self.parse *args
CsvParser.send(:new, *args).send(:parse)
end
private
def parse ...
end
Normally you should not start parsing on initialization - according to the single responsibility principle, a method should do one single thing.

Understanding self with method chaining

I'm trying to understand self in Ruby.
In the code pasted below, if I create a new instance of Animal with
fox = Animal.new.name("Fox").color("red").natural_habitat("forest").specie("mammal")
and then call
fox.to_s
It does not do anything if I do not return self in every method.
Why do I need self in every method? Isn't the variable already saved once I create a new Animal?
class Animal
def name(name)
#name = name
self
end
def specie(specie)
#specie = specie
self
end
def color(color)
#color = color
self
end
def natural_habitat(natural_habitat)
#natural_habitat = natural_habitat
self
end
def to_s
"Name: #{#name}, Specie: #{#specie}, Color: #{#color}, Natural Habitat: #{#natural_habitat}"
end
end
This pattern is used infrequently in Ruby, it's much more common in languages like Java and JavaScript, where it's notably rampant in jQuery. Part of the reason why is the verbosity you're describing here, and secondly because Ruby provides a convenient mutator generator in the form of attr_accessor or attr_writer.
One problem with these accessor/mutator dual purpose methods is ambiguity. The implementation you have is incomplete, you're unable to read from them. What you need is this:
def color(*color)
case (color.length)
when 1
#color = color
self
when 0
#color
else
raise ArgumentError, "wrong number of arguments (%d for 0)" % color.length
end
end
That's a whole lot of code to implement something that can be used in two ways:
animal.color('red')
animal_color = animal.color
If you want to use these, you'll need to write your own meta-programming method that can generate them, though I'd highly discourage going down that path in the first place. Use attr_accessor methods and an options Hash.
Here's the equivalent Ruby pattern:
# Up-front assignment via Hash
animal = Animal.new(
name: 'Fox',
color: 'red',
natural_habitat: 'forest',
specie: 'mammal'
)
# Assignment after the fact
animal.color = 'white'
In your example, using self as the return value is convenient. The methods return the instance itself, so that you can call:
fox = Animal.new
fox.name("Fox").color("red").natural_habitat("forest").specie("mammal")
The value of fox.name("Fox") is the instance itself, that's why you can call .color("red") on it.
if the #name method would be implemented without calling self, like so:
def name(name)
#name = name
end
This method would return a string when called.
Animal.new.name #=> returns "name of animal"
This means that
Animal.new.name.specie
would call #specie method on a string object (which probably raises NotImplemented error) instead of object of Animal class which implements the method.

Resources